Compare commits

...

50 Commits

Author SHA1 Message Date
eitchtee
fa85303f36 chore(locale): update translation files
[skip ci] Automatically generated by Django makemessages workflow
2025-04-13 22:01:20 +00:00
Herculino Trotta
a5f4f43678 Merge pull request #240
feat: user management screen; allow users to edit their profile
2025-04-13 19:00:43 -03:00
Herculino Trotta
d807bd5da3 feat: user management screen; allow users to edit their profile 2025-04-13 19:00:25 -03:00
eitchtee
85314fb749 chore(locale): update translation files
[skip ci] Automatically generated by Django makemessages workflow
2025-04-13 15:49:29 +00:00
Herculino Trotta
c4d5e93a41 Merge pull request #239
feat(transactions): add transaction owner to recurring and installments
2025-04-13 12:48:50 -03:00
Herculino Trotta
86f0c4365e feat(transactions): add transaction owner to recurring and installments 2025-04-13 12:48:18 -03:00
Herculino Trotta
202592b940 Merge pull request #238
fix(transactions): recurring transactions not getting created
2025-04-13 12:47:35 -03:00
Herculino Trotta
aea149bd13 fix(transactions): recurring transactions not getting created 2025-04-13 12:47:01 -03:00
Dimitri Decrock
411365f101 locale(Dutch): update translation
Currently translated at 100.0% (620 of 620 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/nl/
2025-04-13 11:16:53 +00:00
Herculino Trotta
2008476021 locale(Portuguese): update translation
Currently translated at 100.0% (620 of 620 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/pt/
2025-04-13 08:16:53 +00:00
Herculino Trotta
53afe5b8eb locale(Portuguese (Brazil)): update translation
Currently translated at 100.0% (620 of 620 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/pt_BR/
2025-04-13 08:16:53 +00:00
Herculino Trotta
6193c7a048 Merge pull request #237
chore: bump deps
2025-04-13 03:57:45 -03:00
Herculino Trotta
41f81d90d7 chore: bump deps 2025-04-13 03:57:27 -03:00
eitchtee
bf623cf16b chore(locale): update translation files
[skip ci] Automatically generated by Django makemessages workflow
2025-04-13 06:46:15 +00:00
Herculino Trotta
ec213330cd Merge pull request #236
feat(insights:category-overview): add bar chart with category totals
2025-04-13 03:45:43 -03:00
Herculino Trotta
7aedf524c6 feat(insights:category-overview): add bar chart with category totals
Closes #231
2025-04-13 03:45:22 -03:00
eitchtee
04602b1964 chore(locale): update translation files
[skip ci] Automatically generated by Django makemessages workflow
2025-04-13 04:00:29 +00:00
Herculino Trotta
15cfc4f300 Merge pull request #235
locale: add all supported languages by django as an available option
2025-04-13 00:59:51 -03:00
Herculino Trotta
3463c7c62c locale: add all supported languages by django as an available option 2025-04-13 00:58:57 -03:00
Herculino Trotta
7b76c10093 locale(pt): copy strings from pt-br 2025-04-13 00:25:47 -03:00
Herculino Trotta
7ad26a2e7b Merge pull request #234
fix(select): only 50 select options would be shown at a time
2025-04-13 00:03:03 -03:00
Herculino Trotta
7706ca2d5f fix(select): only 50 select options would be shown at a time 2025-04-13 00:02:23 -03:00
Herculino Trotta
56198e93ce locale((Portuguese)): added translation using Weblate 2025-04-13 02:50:39 +00:00
Herculino Trotta
a74323f739 locale((English (United Kingdom))): deleted translation using Weblate 2025-04-13 02:50:09 +00:00
Herculino Trotta
e4efde177b locale((English (Australia))): deleted translation using Weblate 2025-04-13 02:49:54 +00:00
Herculino Trotta
5871a03ee2 locale((Portuguese)): deleted translation using Weblate 2025-04-13 02:49:10 +00:00
Prefill add-on
67af4430e1 locale(Portuguese): update translation
Currently translated at 0.0% (0 of 618 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/pt/
2025-04-13 02:40:11 +00:00
Prefill add-on
696dcdf951 locale(English (United Kingdom)): update translation
Currently translated at 0.0% (0 of 618 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/en_GB/
2025-04-13 02:40:10 +00:00
Prefill add-on
e35bad0e08 locale(English (Australia)): update translation
Currently translated at 0.0% (0 of 618 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/en_AU/
2025-04-13 02:40:09 +00:00
Prefill add-on
904f7cac22 locale(Spanish): update translation
Currently translated at 0.0% (0 of 618 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/es/
2025-04-13 02:40:09 +00:00
Prefill add-on
ccd73963ca locale(French): update translation
Currently translated at 27.6% (171 of 618 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/fr/
2025-04-13 02:40:08 +00:00
Prefill add-on
b5469b0413 locale(German): update translation
Currently translated at 98.7% (610 of 618 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/de/
2025-04-13 02:40:07 +00:00
Herculino Trotta
dae848d951 locale((Portuguese)): added translation using Weblate 2025-04-13 02:34:18 +00:00
Herculino Trotta
45a33ad0c0 locale((English (United Kingdom))): added translation using Weblate 2025-04-13 02:33:05 +00:00
Herculino Trotta
89e50b17bd locale((English (Australia))): added translation using Weblate 2025-04-13 02:33:05 +00:00
Herculino Trotta
ac54ba3da1 Merge pull request #233
fix(cotton): sometimes distribution bars don't get filled completely
2025-04-12 16:33:47 -03:00
Herculino Trotta
2da610f15e fix(cotton): sometimes distribution bars don't get filled completely 2025-04-12 16:33:22 -03:00
valentin-p
4ab6c4c6b6 locale(French): update translation
Currently translated at 27.6% (171 of 618 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/fr/
2025-04-07 08:16:53 +00:00
H4ndrew
68dbedd938 locale(French): update translation
Currently translated at 26.5% (164 of 618 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/fr/
2025-04-03 15:16:53 +00:00
Herculino Trotta
2800c53346 Create FUNDING.yml 2025-04-02 23:03:33 -03:00
Herculino Trotta
132547a074 locale((Spanish)): added translation using Weblate 2025-04-03 01:42:19 +00:00
Herculino Trotta
61ed87dc45 locale(Portuguese (Brazil)): update translation
Currently translated at 100.0% (618 of 618 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/pt_BR/
2025-04-02 20:16:53 +00:00
H4ndrew
96c1227c4f locale(French): update translation
Currently translated at 24.2% (150 of 618 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/fr/
2025-04-02 17:07:17 +00:00
H4ndrew
33f1ac1785 locale(French): update translation
Currently translated at 19.4% (120 of 618 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/fr/
2025-04-02 15:07:17 +00:00
Dimitri Decrock
e9e94a8343 locale(Dutch): update translation
Currently translated at 100.0% (618 of 618 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/nl/
2025-04-01 06:07:17 +00:00
Herculino Trotta
ba24a53853 Merge pull request #229
feat(demo): add dummy data
2025-04-01 01:06:02 -03:00
Herculino Trotta
4955fbde33 feat(demo): add dummy data 2025-04-01 01:05:28 -03:00
eitchtee
d04067a91d chore(locale): update translation files
[skip ci] Automatically generated by Django makemessages workflow
2025-04-01 04:05:11 +00:00
Herculino Trotta
01333a439b Merge pull request #228
fix(common:fields:month_year): unable to load data with a date
2025-04-01 01:04:35 -03:00
Herculino Trotta
d26907ea94 fix(common:fields:month_year): unable to load data with a date 2025-04-01 01:04:20 -03:00
30 changed files with 11252 additions and 1039 deletions

4
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,4 @@
# These are supported funding model platforms
github: eitchtee
custom: ["https://www.paypal.com/donate/?hosted_button_id=FFWM4W9NQDMM6"]

View File

@@ -163,10 +163,105 @@ AUTH_USER_MODEL = "users.User"
LANGUAGE_CODE = "en"
LANGUAGES = (
("af", "Afrikaans"),
("ar", "العربية"),
("ar-dz", "العربية (الجزائر)"), # Algerian Arabic often uses the base name + region
("ast", "Asturianu"),
("az", "Azərbaycan"),
("bg", "Български"),
("be", "Беларуская"),
("bn", "বাংলা"),
("br", "Brezhoneg"),
("bs", "Bosanski"),
("ca", "Català"),
("ckb", "کوردیی ناوەندی"), # Central Kurdish (Sorani)
("cs", "Čeština"),
("cy", "Cymraeg"),
("da", "Dansk"),
("de", "Deutsch"),
("dsb", "Dolnoserbšćina"),
("el", "Ελληνικά"),
("en", "English"),
("en-au", "English (Australia)"),
("en-gb", "English (UK)"),
("eo", "Esperanto"),
("es", "Español"),
("es-ar", "Español (Argentina)"),
("es-co", "Español (Colombia)"),
("es-mx", "Español (México)"),
("es-ni", "Español (Nicaragua)"),
("es-ve", "Español (Venezuela)"),
("et", "Eesti"),
("eu", "Euskara"),
("fa", "فارسی"),
("fi", "Suomi"),
("fr", "Français"),
("fy", "Frysk"),
("ga", "Gaeilge"),
("gd", "Gàidhlig"),
("gl", "Galego"),
("he", "עברית"),
("hi", "हिन्दी"),
("hr", "Hrvatski"),
("hsb", "Hornjoserbšćina"),
("hu", "Magyar"),
("hy", "Հայերեն"),
("ia", "Interlingua"),
("id", "Bahasa Indonesia"),
("ig", "Igbo"),
("io", "Ido"),
("is", "Íslenska"),
("it", "Italiano"),
("ja", "日本語"),
("ka", "ქართული"),
("kab", "Taqbaylit"),
("kk", "Қазақша"),
("km", "ខ្មែរ"),
("kn", "ಕನ್ನಡ"),
("ko", "한국어"),
("ky", "Кыргызча"),
("lb", "Lëtzebuergesch"),
("lt", "Lietuvių"),
("lv", "Latviešu"),
("mk", "Македонски"),
("ml", "മലയാളം"),
("mn", "Монгол"),
("mr", "मराठी"),
("ms", "Bahasa Melayu"),
("my", "မြန်မာဘာသာ"),
("nb", "Norsk (Bokmål)"),
("ne", "नेपाली"),
("nl", "Nederlands"),
("nn", "Norsk (Nynorsk)"),
("os", "Ирон"), # Ossetic
("pa", "ਪੰਜਾਬੀ"),
("pl", "Polski"),
("pt", "Português"),
("pt-br", "Português (Brasil)"),
("ro", "Română"),
("ru", "Русский"),
("sk", "Slovenčina"),
("sl", "Slovenščina"),
("sq", "Shqip"),
("sr", "Српски"),
("sr-latn", "Srpski (Latinica)"),
("sv", "Svenska"),
("sw", "Kiswahili"),
("ta", "தமிழ்"),
("te", "తెలుగు"),
("tg", "Тоҷикӣ"),
("th", "ไทย"),
("tk", "Türkmençe"),
("tr", "Türkçe"),
("tt", "Татарча"),
("udm", "Удмурт"),
("ug", "ئۇيغۇرچە"),
("uk", "Українська"),
("ur", "اردو"),
("uz", "Oʻzbekcha"),
("vi", "Tiếng Việt"),
("zh-hans", "简体中文"),
("zh-hant", "繁體中文"),
)
TIME_ZONE = os.getenv("TZ", "UTC")

View File

@@ -0,0 +1,78 @@
from functools import wraps
from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.shortcuts import redirect
from django.urls import reverse, NoReverseMatch
def is_superuser(view):
@wraps(view)
def _view(request, *args, **kwargs):
if not request.user.is_superuser:
raise PermissionDenied
return view(request, *args, **kwargs)
return _view
def htmx_login_required(function=None, login_url=None):
"""
Decorator that checks if the user is logged in.
Allows overriding the default login URL.
If the user is not logged in:
- If "hx-request" is present in the request header, it returns a 200 response
with a "HX-Redirect" header containing the determined login URL (including the "next" parameter).
- If "hx-request" is not present, it redirects to the determined login page normally.
Args:
function: The view function to decorate.
login_url: Optional. The URL or URL name to redirect to for login.
Defaults to settings.LOGIN_URL.
"""
def decorator(view_func):
# Simplified @wraps usage - it handles necessary attribute assignments by default
@wraps(view_func)
def wrapped_view(request, *args, **kwargs):
if request.user.is_authenticated:
return view_func(request, *args, **kwargs)
else:
# Determine the login URL
resolved_login_url = login_url
if not resolved_login_url:
resolved_login_url = settings.LOGIN_URL
# Try to reverse the URL name if it's not a path
try:
# Check if it looks like a URL path already
if "/" not in resolved_login_url and "." not in resolved_login_url:
login_url_path = reverse(resolved_login_url)
else:
login_url_path = resolved_login_url
except NoReverseMatch:
# If reverse fails, assume it's already a URL path
login_url_path = resolved_login_url
# Construct the full redirect path with 'next' parameter
# Ensure request.path is URL-encoded if needed, though Django usually handles this
redirect_path = f"{login_url_path}?next={request.get_full_path()}" # Use get_full_path() to include query params
if request.headers.get("hx-request"):
# For HTMX requests, return a 200 with the HX-Redirect header.
response = HttpResponse()
response["HX-Redirect"] = login_url_path
return response
else:
# For regular requests, redirect to the login page.
return redirect(redirect_path)
return wrapped_view
if function:
return decorator(function)
return decorator

View File

@@ -20,7 +20,15 @@ class MonthYearModelField(models.DateField):
# Set the day to 1
return date.replace(day=1).date()
except ValueError:
raise ValidationError(_("Invalid date format. Use YYYY-MM."))
try:
# Also accept YYYY-MM-DD format (for loaddata)
return (
datetime.datetime.strptime(value, "%Y-%m-%d").replace(day=1).date()
)
except ValueError:
raise ValidationError(
_("Invalid date format. Use YYYY-MM or YYYY-MM-DD.")
)
def formfield(self, **kwargs):
kwargs["widget"] = MonthYearWidget

View File

@@ -49,7 +49,7 @@ async def remove_expired_sessions(timestamp=None):
def reset_demo_data(timestamp=None):
"""
Wipes the database and loads fresh demo data if DEMO mode is active.
Runs daily at 6:00 AM.
Runs daily at 8:00 AM.
"""
if not settings.DEMO:
return # Exit if not in demo mode
@@ -59,13 +59,14 @@ def reset_demo_data(timestamp=None):
try:
# 1. Flush the database (wipe all data)
logger.info("Flushing the database...")
# Using --noinput prevents prompts. Specify database if not default.
management.call_command(
"flush", "--noinput", database=DEFAULT_DB_ALIAS, verbosity=1
)
logger.info("Database flushed successfully.")
# 2. Load data from the fixture
# TO-DO: Roll dates over based on today's date
fixture_name = "fixtures/demo_data.json"
logger.info(f"Loading data from fixture: {fixture_name}...")
management.call_command(

View File

@@ -15,10 +15,11 @@ from cachalot.api import invalidate
from apps.common.decorators.htmx import only_htmx
from apps.transactions.models import Transaction
from apps.common.decorators.user import htmx_login_required
@only_htmx
@login_required
@htmx_login_required
@require_http_methods(["GET"])
def toasts(request):
return render(request, "common/fragments/toasts.html")

View File

@@ -1,10 +1,8 @@
import decimal
import json
from collections import defaultdict
from dateutil.relativedelta import relativedelta
from django.contrib.auth.decorators import login_required
from django.db.models import Sum, Avg, F
from django.db.models import Sum
from django.shortcuts import render
from django.utils import timezone
from django.views.decorators.http import require_http_methods
@@ -22,13 +20,13 @@ from apps.insights.utils.category_explorer import (
get_category_sums_by_account,
get_category_sums_by_currency,
)
from apps.insights.utils.category_overview import get_categories_totals
from apps.insights.utils.sankey import (
generate_sankey_data_by_account,
generate_sankey_data_by_currency,
)
from apps.insights.utils.transactions import get_transactions
from apps.transactions.models import TransactionCategory, Transaction
from apps.insights.utils.category_overview import get_categories_totals
from apps.transactions.utils.calculations import calculate_currency_totals

View File

@@ -574,6 +574,7 @@ class InstallmentPlan(models.Model):
installment_plan=self,
installment_id=i,
notes=self.notes if self.add_notes_to_transaction else "",
owner=self.account.owner,
)
new_transaction.tags.set(self.tags.all())
new_transaction.entities.set(self.entities.all())
@@ -640,6 +641,7 @@ class InstallmentPlan(models.Model):
installment_plan=self,
installment_id=i,
notes=self.notes if self.add_notes_to_transaction else "",
owner=self.account.owner,
)
new_transaction.tags.set(self.tags.all())
new_transaction.entities.set(self.entities.all())
@@ -775,6 +777,7 @@ class RecurringTransaction(models.Model):
is_paid=False,
recurring_transaction=self,
notes=self.notes if self.add_notes_to_transaction else "",
owner=self.account.owner,
)
if self.tags.exists():
created_transaction.tags.set(self.tags.all())
@@ -797,12 +800,16 @@ class RecurringTransaction(models.Model):
@classmethod
def generate_upcoming_transactions(cls):
today = timezone.now().date()
recurring_transactions = cls.objects.filter(
recurring_transactions = cls.all_objects.filter(
Q(models.Q(end_date__isnull=True) | Q(end_date__gte=today))
& Q(is_paused=False)
)
for recurring_transaction in recurring_transactions:
logger.info(
f"Processing recurring transaction: {recurring_transaction.description}..."
)
if recurring_transaction.last_generated_date:
start_date = recurring_transaction.get_next_date(
recurring_transaction.last_generated_date
@@ -821,7 +828,10 @@ class RecurringTransaction(models.Model):
today + (recurring_transaction.get_recurrence_delta() * 6),
)
logger.info(f"End date: {end_date}")
while current_date <= end_date:
logger.info(f"Creating transaction for date: {current_date}")
recurring_transaction.create_transaction(current_date, reference_date)
current_date = recurring_transaction.get_next_date(current_date)
reference_date = recurring_transaction.get_next_date(reference_date)

View File

@@ -2,16 +2,20 @@ from crispy_forms.bootstrap import (
FormActions,
)
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Submit
from crispy_forms.layout import Layout, Submit, Row, Column, Field, Div
from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import (
UsernameField,
AuthenticationForm,
UserCreationForm,
)
from django.db import transaction
from django.utils.translation import gettext_lazy as _
from apps.common.widgets.crispy.submit import NoClassSubmit
from apps.users.models import UserSettings
from apps.common.middleware.thread_local import get_current_user
class LoginForm(AuthenticationForm):
@@ -132,3 +136,269 @@ class UserSettingsForm(forms.ModelForm):
),
),
)
self.fields["language"].help_text = _(
"This changes the language (if available) and how numbers and dates are displayed\n"
"Consider helping translate WYGIWYH to your language at %(translation_link)s"
) % {
"translation_link": '<a href="https://translations.herculino.com" target="_blank">translations.herculino.com</a>'
}
class UserUpdateForm(forms.ModelForm):
new_password1 = forms.CharField(
label=_("New Password"),
widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
required=False,
help_text=_("Leave blank to keep the current password."),
)
new_password2 = forms.CharField(
label=_("Confirm New Password"),
widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
required=False,
)
class Meta:
model = get_user_model()
# Add the administrative fields
fields = ["first_name", "last_name", "email", "is_active", "is_superuser"]
# Help texts can be defined here or directly in the layout/field definition
help_texts = {
"is_active": _(
"Designates whether this user should be treated as active. Unselect this instead of deleting accounts."
),
"is_superuser": _(
"Designates that this user has all permissions without explicitly assigning them."
),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.instance = kwargs.get("instance") # Store instance for validation/checks
self.requesting_user = get_current_user()
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.form_method = "post"
# Define the layout using Crispy Forms, including the new fields
self.helper.layout = Layout(
Row(
Column("first_name", css_class="form-group col-md-6"),
Column("last_name", css_class="form-group col-md-6"),
css_class="row",
),
Field("email"),
# Group password fields (optional visual grouping)
Div(
Field("new_password1"),
Field("new_password2"),
css_class="border p-3 rounded mb-3",
),
# Group administrative status fields
Div(
Field("is_active"),
Field("is_superuser"),
css_class="border p-3 rounded mb-3 text-bg-secondary", # Example visual separation
),
)
if self.instance and self.instance.pk:
self.helper.layout.append(
FormActions(
NoClassSubmit(
"submit", _("Update"), css_class="btn btn-outline-primary w-100"
),
),
)
else:
self.helper.layout.append(
FormActions(
NoClassSubmit(
"submit", _("Add"), css_class="btn btn-outline-primary w-100"
),
),
)
if (
self.requesting_user == self.instance
or not self.requesting_user.is_superuser
):
self.fields["is_superuser"].disabled = True
self.fields["is_active"].disabled = True
# Keep existing clean methods
def clean_email(self):
email = self.cleaned_data.get("email")
# Use case-insensitive comparison for email uniqueness check
if (
self.instance
and get_user_model()
.objects.filter(email__iexact=email)
.exclude(pk=self.instance.pk)
.exists()
):
raise forms.ValidationError(
_("This email address is already in use by another account.")
)
return email
def clean_new_password2(self):
new_password1 = self.cleaned_data.get("new_password1")
new_password2 = self.cleaned_data.get("new_password2")
if new_password1 and new_password1 != new_password2:
raise forms.ValidationError(_("The two password fields didn't match."))
if new_password1 and not new_password2:
raise forms.ValidationError(_("Please confirm your new password."))
if new_password2 and not new_password1:
raise forms.ValidationError(_("Please enter the new password first."))
return new_password2
def clean(self):
cleaned_data = super().clean()
is_active_val = cleaned_data.get("is_active")
is_superuser_val = cleaned_data.get("is_superuser")
# --- Crucial Security Check Example ---
# Prevent the requesting user from deactivating or removing superuser status
# from their *own* account via this form.
if (
self.requesting_user
and self.instance
and self.requesting_user.pk == self.instance.pk
):
# Check if 'is_active' field exists and user is trying to set it to False
if "is_active" in self.fields and is_active_val is False:
self.add_error(
"is_active",
_("You cannot deactivate your own account using this form."),
)
# Check if 'is_superuser' field exists, the user *is* currently a superuser,
# and they are trying to set it to False
if (
"is_superuser" in self.fields
and self.instance.is_superuser
and is_superuser_val is False
):
if get_user_model().objects.filter(is_superuser=True).count() <= 1:
self.add_error(
"is_superuser",
_("Cannot remove status from the last superuser."),
)
else:
self.add_error(
"is_superuser",
_(
"You cannot remove your own superuser status using this form."
),
)
return cleaned_data
# Save method remains the same, ModelForm handles boolean fields correctly
def save(self, commit=True):
user = super().save(commit=False)
new_password = self.cleaned_data.get("new_password1")
if new_password:
user.set_password(new_password)
if commit:
user.save()
return user
class UserAddForm(UserCreationForm):
"""
A form for administrators to create new users.
Includes fields for first name, last name, email, active status,
and superuser status. Uses email as the username field.
Inherits password handling from UserCreationForm.
"""
class Meta(UserCreationForm.Meta):
model = get_user_model()
# Specify the fields to include. UserCreationForm automatically handles
# 'password1' and 'password2'. We replace 'username' with 'email'.
fields = ("email", "first_name", "last_name", "is_active", "is_superuser")
field_classes = {
"email": forms.EmailField
} # Ensure email field uses EmailField validation
help_texts = {
"is_active": _(
"Designates whether this user should be treated as active. Unselect this instead of deleting accounts."
),
"is_superuser": _(
"Designates that this user has all permissions without explicitly assigning them."
),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Set is_active to True by default for new users, can be overridden by admin
self.fields["is_active"].initial = True
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.form_method = "post"
# Define the layout, including password fields from UserCreationForm
self.helper.layout = Layout(
Field("email"),
Row(
Column("first_name", css_class="form-group col-md-6"),
Column("last_name", css_class="form-group col-md-6"),
css_class="row",
),
# UserCreationForm provides 'password1' and 'password2' fields
Div(
Field("password1", autocomplete="new-password"),
Field("password2", autocomplete="new-password"),
css_class="border p-3 rounded mb-3",
),
# Administrative status fields
Div(
Field("is_active"),
Field("is_superuser"),
css_class="border p-3 rounded mb-3 text-bg-secondary",
),
)
if self.instance and self.instance.pk:
self.helper.layout.append(
FormActions(
NoClassSubmit(
"submit", _("Update"), css_class="btn btn-outline-primary w-100"
),
),
)
else:
self.helper.layout.append(
FormActions(
NoClassSubmit(
"submit", _("Add"), css_class="btn btn-outline-primary w-100"
),
),
)
def clean_email(self):
"""Ensure email uniqueness (case-insensitive)."""
email = self.cleaned_data.get("email")
if email and get_user_model().objects.filter(email__iexact=email).exists():
raise forms.ValidationError(
_("A user with this email address already exists.")
)
return email
@transaction.atomic # Ensure user creation is atomic
def save(self, commit=True):
"""
Save the user instance. UserCreationForm's save handles password hashing.
Our Meta class ensures other fields are included.
"""
user = super().save(commit=False)
if commit:
user.save()
return user

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.8 on 2025-04-13 03:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0019_alter_usersettings_language'),
]
operations = [
migrations.AlterField(
model_name='usersettings',
name='language',
field=models.CharField(choices=[('auto', 'Auto'), ('af', 'Afrikaans'), ('ar', 'العربية'), ('ar-dz', 'العربية (الجزائر)'), ('ast', 'Asturianu'), ('az', 'Azərbaycan'), ('bg', 'Български'), ('be', 'Беларуская'), ('bn', 'বাংলা'), ('br', 'Brezhoneg'), ('bs', 'Bosanski'), ('ca', 'Català'), ('ckb', 'کوردیی ناوەندی'), ('cs', 'Čeština'), ('cy', 'Cymraeg'), ('da', 'Dansk'), ('de', 'Deutsch'), ('dsb', 'Dolnoserbšćina'), ('el', 'Ελληνικά'), ('en', 'English'), ('en-au', 'English (Australia)'), ('en-gb', 'English (UK)'), ('eo', 'Esperanto'), ('es', 'Español'), ('es-ar', 'Español (Argentina)'), ('es-co', 'Español (Colombia)'), ('es-mx', 'Español (México)'), ('es-ni', 'Español (Nicaragua)'), ('es-ve', 'Español (Venezuela)'), ('et', 'Eesti'), ('eu', 'Euskara'), ('fa', 'فارسی'), ('fi', 'Suomi'), ('fr', 'Français'), ('fy', 'Frysk'), ('ga', 'Gaeilge'), ('gd', 'Gàidhlig'), ('gl', 'Galego'), ('he', 'עברית'), ('hi', 'हिन्दी'), ('hr', 'Hrvatski'), ('hsb', 'Hornjoserbšćina'), ('hu', 'Magyar'), ('hy', 'Հայերեն'), ('ia', 'Interlingua'), ('id', 'Bahasa Indonesia'), ('ig', 'Igbo'), ('io', 'Ido'), ('is', 'Íslenska'), ('it', 'Italiano'), ('ja', '日本語'), ('ka', 'ქართული'), ('kab', 'Taqbaylit'), ('kk', 'Қазақша'), ('km', 'ខ្មែរ'), ('kn', 'ಕನ್ನಡ'), ('ko', '한국어'), ('ky', 'Кыргызча'), ('lb', 'Lëtzebuergesch'), ('lt', 'Lietuvių'), ('lv', 'Latviešu'), ('mk', 'Македонски'), ('ml', 'മലയാളം'), ('mn', 'Монгол'), ('mr', 'मराठी'), ('ms', 'Bahasa Melayu'), ('my', 'မြန်မာဘာသာ'), ('nb', 'Norsk (Bokmål)'), ('ne', 'नेपाली'), ('nl', 'Nederlands'), ('nn', 'Norsk (Nynorsk)'), ('os', 'Ирон'), ('pa', 'ਪੰਜਾਬੀ'), ('pl', 'Polski'), ('pt', 'Português'), ('pt-br', 'Português (Brasil)'), ('ro', 'Română'), ('ru', 'Русский'), ('sk', 'Slovenčina'), ('sl', 'Slovenščina'), ('sq', 'Shqip'), ('sr', 'Српски'), ('sr-latn', 'Srpski (Latinica)'), ('sv', 'Svenska'), ('sw', 'Kiswahili'), ('ta', 'தமிழ்'), ('te', 'తెలుగు'), ('tg', 'Тоҷикӣ'), ('th', 'ไทย'), ('tk', 'Türkmençe'), ('tr', 'Türkçe'), ('tt', 'Татарча'), ('udm', 'Удмурт'), ('ug', 'ئۇيغۇرچە'), ('uk', 'Українська'), ('ur', 'اردو'), ('uz', 'Oʻzbekcha'), ('vi', 'Tiếng Việt'), ('zh-hans', '简体中文'), ('zh-hant', '繁體中文')], default='auto', max_length=10, verbose_name='Language'),
),
]

View File

@@ -22,4 +22,24 @@ urlpatterns = [
views.update_settings,
name="user_settings",
),
path(
"users/",
views.users_index,
name="users_index",
),
path(
"users/list/",
views.users_list,
name="users_list",
),
path(
"user/add/",
views.user_add,
name="user_add",
),
path(
"user/<int:pk>/edit/",
views.user_edit,
name="user_edit",
),
]

View File

@@ -1,19 +1,24 @@
from django.contrib import messages
from django.contrib.auth import logout
from django.contrib.auth import logout, get_user_model
from django.contrib.auth.decorators import login_required
from django.contrib.auth.views import (
LoginView,
)
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.shortcuts import redirect, render
from django.shortcuts import redirect, render, get_object_or_404
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.views.decorators.http import require_http_methods
from apps.common.decorators.htmx import only_htmx
from apps.common.decorators.user import is_superuser, htmx_login_required
from apps.users.forms import (
LoginForm,
UserSettingsForm,
UserUpdateForm,
UserAddForm,
)
from apps.common.decorators.htmx import only_htmx
from apps.users.models import UserSettings
@@ -22,7 +27,7 @@ def logout_view(request):
return redirect(reverse("login"))
@login_required
@htmx_login_required
def index(request):
if request.user.settings.start_page == UserSettings.StartPage.MONTHLY:
return redirect(reverse("monthly_index"))
@@ -49,7 +54,7 @@ class UserLoginView(LoginView):
@only_htmx
@login_required
@htmx_login_required
def toggle_amount_visibility(request):
user_settings, created = UserSettings.objects.get_or_create(user=request.user)
current_hide_amounts = user_settings.hide_amounts
@@ -70,7 +75,7 @@ def toggle_amount_visibility(request):
@only_htmx
@login_required
@htmx_login_required
def toggle_sound_playing(request):
user_settings, created = UserSettings.objects.get_or_create(user=request.user)
current_mute_sounds = user_settings.mute_sounds
@@ -91,7 +96,7 @@ def toggle_sound_playing(request):
@only_htmx
@login_required
@htmx_login_required
def update_settings(request):
user_settings = request.user.settings
@@ -108,3 +113,85 @@ def update_settings(request):
form = UserSettingsForm(instance=user_settings)
return render(request, "users/fragments/user_settings.html", {"form": form})
@htmx_login_required
@is_superuser
@require_http_methods(["GET"])
def users_index(request):
return render(
request,
"users/pages/index.html",
)
@only_htmx
@htmx_login_required
@is_superuser
@require_http_methods(["GET"])
def users_list(request):
users = get_user_model().objects.all().order_by("id")
return render(
request,
"users/fragments/list.html",
{"users": users},
)
@only_htmx
@htmx_login_required
@is_superuser
@require_http_methods(["GET", "POST"])
def user_add(request):
if request.method == "POST":
form = UserAddForm(request.POST)
if form.is_valid():
form.save()
messages.success(request, _("Item added successfully"))
return HttpResponse(
status=204,
headers={
"HX-Trigger": "updated, hide_offcanvas",
},
)
else:
form = UserAddForm()
return render(
request,
"users/fragments/add.html",
{"form": form},
)
@only_htmx
@htmx_login_required
@require_http_methods(["GET", "POST"])
def user_edit(request, pk):
user = get_object_or_404(get_user_model(), id=pk)
if not request.user.is_superuser and user != request.user:
raise PermissionDenied
if request.method == "POST":
form = UserUpdateForm(request.POST, instance=user)
if form.is_valid():
form.save()
messages.success(request, _("Item updated successfully"))
return HttpResponse(
status=204,
headers={
"HX-Trigger": "updated, hide_offcanvas",
},
)
else:
form = UserUpdateForm(instance=user)
return render(
request,
"users/fragments/edit.html",
{"form": form, "user": user},
)

File diff suppressed because it is too large Load Diff

View File

@@ -7,9 +7,9 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-03-31 05:30+0000\n"
"PO-Revision-Date: 2025-03-30 15:07+0000\n"
"Last-Translator: valentin-p <valentinpouget@gmail.com>\n"
"POT-Creation-Date: 2025-04-13 22:01+0000\n"
"PO-Revision-Date: 2025-04-13 02:40+0000\n"
"Last-Translator: Prefill add-on <noreply-addon-prefill@weblate.org>\n"
"Language-Team: German <https://translations.herculino.com/projects/wygiwyh/"
"app/de/>\n"
"Language: de\n"
@@ -31,6 +31,7 @@ msgstr "Gruppe Name"
#: apps/transactions/forms.py:269 apps/transactions/forms.py:629
#: apps/transactions/forms.py:672 apps/transactions/forms.py:704
#: apps/transactions/forms.py:739 apps/transactions/forms.py:891
#: apps/users/forms.py:210 apps/users/forms.py:372
msgid "Update"
msgstr "Aktualisierung"
@@ -42,8 +43,8 @@ msgstr "Aktualisierung"
#: apps/transactions/forms.py:187 apps/transactions/forms.py:211
#: apps/transactions/forms.py:637 apps/transactions/forms.py:680
#: apps/transactions/forms.py:712 apps/transactions/forms.py:747
#: apps/transactions/forms.py:899
#: templates/account_groups/fragments/list.html:9
#: apps/transactions/forms.py:899 apps/users/forms.py:218
#: apps/users/forms.py:380 templates/account_groups/fragments/list.html:9
#: templates/accounts/fragments/list.html:9
#: templates/categories/fragments/list.html:9
#: templates/currencies/fragments/list.html:9
@@ -58,6 +59,7 @@ msgstr "Aktualisierung"
#: templates/mini_tools/unit_price_calculator.html:162
#: templates/recurring_transactions/fragments/list.html:9
#: templates/rules/fragments/list.html:9 templates/tags/fragments/list.html:9
#: templates/users/fragments/list.html:10
msgid "Add"
msgstr "Hinzufügen"
@@ -75,8 +77,9 @@ msgstr "Neuer Saldo"
#: apps/transactions/forms.py:40 apps/transactions/forms.py:303
#: apps/transactions/forms.py:310 apps/transactions/forms.py:510
#: apps/transactions/forms.py:771 apps/transactions/models.py:305
#: apps/transactions/models.py:488 apps/transactions/models.py:686
#: templates/insights/fragments/category_overview/index.html:9
#: apps/transactions/models.py:488 apps/transactions/models.py:688
#: templates/insights/fragments/category_overview/index.html:10
#: templates/insights/fragments/category_overview/index.html:215
msgid "Category"
msgstr "Kategorie"
@@ -87,7 +90,7 @@ msgstr "Kategorie"
#: apps/transactions/forms.py:48 apps/transactions/forms.py:319
#: apps/transactions/forms.py:327 apps/transactions/forms.py:503
#: apps/transactions/forms.py:764 apps/transactions/models.py:311
#: apps/transactions/models.py:490 apps/transactions/models.py:690
#: apps/transactions/models.py:490 apps/transactions/models.py:692
#: templates/includes/navbar.html:108 templates/tags/fragments/list.html:5
#: templates/tags/pages/index.html:4
msgid "Tags"
@@ -108,6 +111,7 @@ msgstr "Tags"
#: templates/recurring_transactions/fragments/table.html:18
#: templates/rules/fragments/list.html:26
#: templates/tags/fragments/table.html:16
#: templates/users/fragments/list.html:29
msgid "Name"
msgstr "Name"
@@ -162,7 +166,7 @@ msgstr ""
#: apps/rules/models.py:30 apps/rules/models.py:242
#: apps/transactions/forms.py:60 apps/transactions/forms.py:495
#: apps/transactions/forms.py:756 apps/transactions/models.py:278
#: apps/transactions/models.py:448 apps/transactions/models.py:668
#: apps/transactions/models.py:448 apps/transactions/models.py:670
msgid "Account"
msgstr "Konto"
@@ -286,7 +290,13 @@ msgstr "Fehler bei der Erstellung einer neuen Instanz"
msgid "Ungrouped"
msgstr "Ohne Gruppierung"
#: apps/common/fields/month_year.py:23 apps/common/fields/month_year.py:51
#: apps/common/fields/month_year.py:30
#, fuzzy
#| msgid "Invalid date format. Use YYYY-MM."
msgid "Invalid date format. Use YYYY-MM or YYYY-MM-DD."
msgstr "Ungültiges Datumsformat. Nutze JJJJ-MM."
#: apps/common/fields/month_year.py:59
msgid "Invalid date format. Use YYYY-MM."
msgstr "Ungültiges Datumsformat. Nutze JJJJ-MM."
@@ -322,7 +332,7 @@ msgstr ""
"Privat: Nur für den Besitzer und geteilte Nutzer sichtbar.<br/>Öffentlich: "
"Sichtbar für alle Nutzer. Nur bearbeitbar durch Besitzer."
#: apps/common/forms.py:79 apps/users/forms.py:131
#: apps/common/forms.py:79 apps/users/forms.py:135
msgid "Save"
msgstr "Speichern"
@@ -413,7 +423,7 @@ msgstr "Fehler"
msgid "Info"
msgstr "Info"
#: apps/common/views.py:110
#: apps/common/views.py:111
msgid "Cache cleared successfully"
msgstr "Cache erfolgreich geleert"
@@ -538,7 +548,7 @@ msgstr "Diensttyp"
#: templates/categories/fragments/list.html:21
#: templates/entities/fragments/list.html:21
#: templates/recurring_transactions/fragments/list.html:21
#: templates/tags/fragments/list.html:21
#: templates/tags/fragments/list.html:21 templates/users/fragments/list.html:28
msgid "Active"
msgstr "Aktiv"
@@ -713,7 +723,7 @@ msgstr "Startwährung"
#: apps/dca/models.py:26 apps/dca/models.py:181 apps/rules/forms.py:173
#: apps/rules/forms.py:188 apps/rules/models.py:37 apps/rules/models.py:270
#: apps/transactions/forms.py:347 apps/transactions/models.py:301
#: apps/transactions/models.py:497 apps/transactions/models.py:696
#: apps/transactions/models.py:497 apps/transactions/models.py:698
msgid "Notes"
msgstr "Notizen"
@@ -770,6 +780,8 @@ msgid "Entry deleted successfully"
msgstr "Eintrag erfolgreich gelöscht"
#: apps/export_app/forms.py:14 apps/export_app/forms.py:131
#: templates/includes/navbar.html:147 templates/users/fragments/list.html:6
#: templates/users/pages/index.html:4
msgid "Users"
msgstr "Nutzer"
@@ -794,13 +806,13 @@ msgstr "Kategorien"
#: apps/transactions/forms.py:56 apps/transactions/forms.py:518
#: apps/transactions/forms.py:779 apps/transactions/models.py:261
#: apps/transactions/models.py:316 apps/transactions/models.py:493
#: apps/transactions/models.py:693 templates/entities/fragments/list.html:5
#: apps/transactions/models.py:695 templates/entities/fragments/list.html:5
#: templates/entities/pages/index.html:4 templates/includes/navbar.html:110
msgid "Entities"
msgstr "Entitäten"
#: apps/export_app/forms.py:56 apps/export_app/forms.py:140
#: apps/transactions/models.py:730 templates/includes/navbar.html:74
#: apps/transactions/models.py:732 templates/includes/navbar.html:74
#: templates/recurring_transactions/fragments/list.html:5
#: templates/recurring_transactions/pages/index.html:4
msgid "Recurring Transactions"
@@ -962,7 +974,9 @@ msgstr "Vorgang erfolgreich gelöscht"
#: apps/insights/forms.py:119 apps/insights/utils/sankey.py:36
#: apps/insights/utils/sankey.py:167
#: templates/insights/fragments/category_overview/index.html:18
#: templates/insights/fragments/category_overview/index.html:19
#: templates/insights/fragments/category_overview/index.html:87
#: templates/insights/fragments/category_overview/index.html:116
msgid "Uncategorized"
msgstr "Unkategorisiert"
@@ -1045,7 +1059,7 @@ msgstr "Bediener"
#: apps/rules/forms.py:167 apps/rules/forms.py:180 apps/rules/models.py:31
#: apps/rules/models.py:246 apps/transactions/models.py:285
#: apps/transactions/models.py:453 apps/transactions/models.py:674
#: apps/transactions/models.py:453 apps/transactions/models.py:676
msgid "Type"
msgstr "Typ"
@@ -1062,20 +1076,20 @@ msgstr "Bezahlt"
#: apps/rules/models.py:258 apps/transactions/forms.py:68
#: apps/transactions/forms.py:334 apps/transactions/forms.py:524
#: apps/transactions/models.py:289 apps/transactions/models.py:471
#: apps/transactions/models.py:698
#: apps/transactions/models.py:700
msgid "Reference Date"
msgstr "Referenzdatum"
#: apps/rules/forms.py:171 apps/rules/forms.py:184 apps/rules/models.py:35
#: apps/rules/models.py:262 apps/transactions/models.py:294
#: apps/transactions/models.py:679 templates/insights/fragments/sankey.html:95
#: apps/transactions/models.py:681 templates/insights/fragments/sankey.html:95
msgid "Amount"
msgstr "Betrag"
#: apps/rules/forms.py:172 apps/rules/forms.py:185 apps/rules/models.py:14
#: apps/rules/models.py:36 apps/rules/models.py:266
#: apps/transactions/forms.py:338 apps/transactions/models.py:299
#: apps/transactions/models.py:455 apps/transactions/models.py:682
#: apps/transactions/models.py:455 apps/transactions/models.py:684
msgid "Description"
msgstr "Beschreibung"
@@ -1339,7 +1353,7 @@ msgstr "Entität"
#: templates/calendar_view/fragments/list.html:52
#: templates/calendar_view/fragments/list.html:54
#: templates/cotton/ui/quick_transactions_buttons.html:10
#: templates/insights/fragments/category_overview/index.html:10
#: templates/insights/fragments/category_overview/index.html:11
#: templates/monthly_overview/fragments/monthly_summary.html:39
msgid "Income"
msgstr "Einnahme"
@@ -1350,7 +1364,7 @@ msgstr "Einnahme"
#: templates/calendar_view/fragments/list.html:56
#: templates/calendar_view/fragments/list.html:58
#: templates/cotton/ui/quick_transactions_buttons.html:18
#: templates/insights/fragments/category_overview/index.html:11
#: templates/insights/fragments/category_overview/index.html:12
msgid "Expense"
msgstr "Ausgabe"
@@ -1358,7 +1372,7 @@ msgstr "Ausgabe"
msgid "Installment Plan"
msgstr "Ratenzahlungs-Plan"
#: apps/transactions/models.py:336 apps/transactions/models.py:729
#: apps/transactions/models.py:336 apps/transactions/models.py:731
msgid "Recurring Transaction"
msgstr "Wiederkehrende Transaktion"
@@ -1416,11 +1430,11 @@ msgid "The installment number to start counting from"
msgstr ""
"Die Zahl mit der bei der Zählung der Ratenzahlungen begonnen werden soll"
#: apps/transactions/models.py:469 apps/transactions/models.py:702
#: apps/transactions/models.py:469 apps/transactions/models.py:704
msgid "Start Date"
msgstr "Startdatum"
#: apps/transactions/models.py:473 apps/transactions/models.py:703
#: apps/transactions/models.py:473 apps/transactions/models.py:705
msgid "End Date"
msgstr "Enddatum"
@@ -1432,48 +1446,48 @@ msgstr "Regelmäßigkeit"
msgid "Installment Amount"
msgstr "Ratenzahlungs-Wert"
#: apps/transactions/models.py:500 apps/transactions/models.py:719
#: apps/transactions/models.py:500 apps/transactions/models.py:721
msgid "Add description to transactions"
msgstr "Beschreibung zu Transaktionen hinzufügen"
#: apps/transactions/models.py:503 apps/transactions/models.py:722
#: apps/transactions/models.py:503 apps/transactions/models.py:724
msgid "Add notes to transactions"
msgstr "Notizen zu Transaktionen hinzufügen"
#: apps/transactions/models.py:661
#: apps/transactions/models.py:663
msgid "day(s)"
msgstr "Tag(e)"
#: apps/transactions/models.py:662
#: apps/transactions/models.py:664
msgid "week(s)"
msgstr "Woche(n)"
#: apps/transactions/models.py:663
#: apps/transactions/models.py:665
msgid "month(s)"
msgstr "Monat(e)"
#: apps/transactions/models.py:664
#: apps/transactions/models.py:666
msgid "year(s)"
msgstr "Jahr(e)"
#: apps/transactions/models.py:666
#: apps/transactions/models.py:668
#: templates/recurring_transactions/fragments/list.html:24
msgid "Paused"
msgstr "Pausiert"
#: apps/transactions/models.py:705
#: apps/transactions/models.py:707
msgid "Recurrence Type"
msgstr "Regelmäßigkeit"
#: apps/transactions/models.py:708
#: apps/transactions/models.py:710
msgid "Recurrence Interval"
msgstr "Wiederholungsintervall"
#: apps/transactions/models.py:712
#: apps/transactions/models.py:714
msgid "Last Generated Date"
msgstr "Letztes generiertes Datum"
#: apps/transactions/models.py:715
#: apps/transactions/models.py:717
msgid "Last Generated Reference Date"
msgstr "Letztes generiertes Referenzdatum"
@@ -1650,40 +1664,108 @@ msgstr "Berechtigungen"
msgid "Important dates"
msgstr "Wichtige Daten"
#: apps/users/forms.py:19 apps/users/models.py:13 templates/users/login.html:19
#: apps/users/forms.py:23 apps/users/models.py:13 templates/users/login.html:19
msgid "E-mail"
msgstr "E-Mail"
#: apps/users/forms.py:25 templates/users/login.html:20
#: apps/users/forms.py:29 templates/users/login.html:20
msgid "Password"
msgstr "Passwort"
#: apps/users/forms.py:33
#: apps/users/forms.py:37
msgid "Invalid e-mail or password"
msgstr "Ungültige E-Mail oder Passwort"
#: apps/users/forms.py:34
#: apps/users/forms.py:38
msgid "This account is deactivated"
msgstr "Dieses Konto ist deaktiviert"
#: apps/users/forms.py:50 apps/users/forms.py:63 apps/users/forms.py:85
#: apps/users/forms.py:54 apps/users/forms.py:67 apps/users/forms.py:89
#: templates/monthly_overview/pages/overview.html:153
#: templates/transactions/pages/transactions.html:35
msgid "Default"
msgstr "Standard"
#: apps/users/forms.py:91 apps/users/models.py:41
#: apps/users/forms.py:95 apps/users/models.py:41
msgid "Date Format"
msgstr "Datumsformat"
#: apps/users/forms.py:96 apps/users/models.py:46
#: apps/users/forms.py:100 apps/users/models.py:46
msgid "Datetime Format"
msgstr "Datums- und Zeitformat"
#: apps/users/forms.py:102 apps/users/models.py:49
#: apps/users/forms.py:106 apps/users/models.py:49
msgid "Number Format"
msgstr "Zahlenformat"
#: apps/users/forms.py:141
#, python-format
msgid ""
"This changes the language (if available) and how numbers and dates are "
"displayed\n"
"Consider helping translate WYGIWYH to your language at %(translation_link)s"
msgstr ""
#: apps/users/forms.py:150
#, fuzzy
#| msgid "Password"
msgid "New Password"
msgstr "Passwort"
#: apps/users/forms.py:153
msgid "Leave blank to keep the current password."
msgstr ""
#: apps/users/forms.py:156
msgid "Confirm New Password"
msgstr ""
#: apps/users/forms.py:168 apps/users/forms.py:329
msgid ""
"Designates whether this user should be treated as active. Unselect this "
"instead of deleting accounts."
msgstr ""
#: apps/users/forms.py:171 apps/users/forms.py:332
msgid ""
"Designates that this user has all permissions without explicitly assigning "
"them."
msgstr ""
#: apps/users/forms.py:242
msgid "This email address is already in use by another account."
msgstr ""
#: apps/users/forms.py:250
msgid "The two password fields didn't match."
msgstr ""
#: apps/users/forms.py:252
msgid "Please confirm your new password."
msgstr ""
#: apps/users/forms.py:254
msgid "Please enter the new password first."
msgstr ""
#: apps/users/forms.py:274
msgid "You cannot deactivate your own account using this form."
msgstr ""
#: apps/users/forms.py:287
msgid "Cannot remove status from the last superuser."
msgstr ""
#: apps/users/forms.py:293
msgid "You cannot remove your own superuser status using this form."
msgstr ""
#: apps/users/forms.py:390
#, fuzzy
#| msgid "A value for this field already exists in the rule."
msgid "A user with this email address already exists."
msgstr "Ein Wert für dieses Feld existiert bereits in dieser Regel."
#: apps/users/models.py:27 templates/includes/navbar.html:28
msgid "Yearly by currency"
msgstr "Jährlich nach Währung"
@@ -1724,26 +1806,38 @@ msgstr "Zeitzone"
msgid "Start page"
msgstr "Startseite"
#: apps/users/views.py:62
#: apps/users/views.py:67
msgid "Transaction amounts are now hidden"
msgstr "Beträge sind nun versteckt"
#: apps/users/views.py:65
#: apps/users/views.py:70
msgid "Transaction amounts are now displayed"
msgstr "Beträge werden angezeigt"
#: apps/users/views.py:83
#: apps/users/views.py:88
msgid "Sounds are now muted"
msgstr "Sounds sind stummgeschaltet"
#: apps/users/views.py:86
#: apps/users/views.py:91
msgid "Sounds will now play"
msgstr "Sounds werden wiedergegeben"
#: apps/users/views.py:102
#: apps/users/views.py:107
msgid "Your settings have been updated"
msgstr "Deine Einstellungen wurden aktualisiert"
#: apps/users/views.py:151
#, fuzzy
#| msgid "Rule added successfully"
msgid "Item added successfully"
msgstr "Regel erfolgreich hinzugefügt"
#: apps/users/views.py:182
#, fuzzy
#| msgid "Rule updated successfully"
msgid "Item updated successfully"
msgstr "Regel erfolgreich aktualisiert"
#: templates/account_groups/fragments/add.html:5
msgid "Add account group"
msgstr "Kontogruppe hinzufügen"
@@ -1766,6 +1860,7 @@ msgstr "Kontogruppe bearbeiten"
#: templates/recurring_transactions/fragments/table.html:25
#: templates/rules/fragments/list.html:33
#: templates/tags/fragments/table.html:23
#: templates/users/fragments/list.html:38
msgid "Actions"
msgstr "Aktionen"
@@ -1788,6 +1883,7 @@ msgstr "Aktionen"
#: templates/rules/fragments/transaction_rule/view.html:47
#: templates/rules/fragments/transaction_rule/view.html:80
#: templates/tags/fragments/table.html:28
#: templates/users/fragments/list.html:43
msgid "Edit"
msgstr "Bearbeiten"
@@ -2010,7 +2106,7 @@ msgid "Muted"
msgstr "Ausgeblendet"
#: templates/categories/fragments/table.html:75
#: templates/insights/fragments/category_overview/index.html:67
#: templates/insights/fragments/category_overview/index.html:225
msgid "No categories"
msgstr "Keine Kategorien"
@@ -2511,15 +2607,19 @@ msgstr "Verwaltung"
msgid "Automation"
msgstr "Automatisierung"
#: templates/includes/navbar.html:150
#: templates/includes/navbar.html:145
msgid "Admin"
msgstr ""
#: templates/includes/navbar.html:154
msgid "Only use this if you know what you're doing"
msgstr "Nur benutzen, wenn du weißt was du tust"
#: templates/includes/navbar.html:151
#: templates/includes/navbar.html:155
msgid "Django Admin"
msgstr "Django Admin"
#: templates/includes/navbar.html:160
#: templates/includes/navbar.html:165
msgid "Calculator"
msgstr "Rechner"
@@ -2527,22 +2627,31 @@ msgstr "Rechner"
msgid "Settings"
msgstr "Einstellungen"
#: templates/includes/navbar/user_menu.html:41
#: templates/includes/navbar/user_menu.html:19
#, fuzzy
#| msgid "Edit import profile"
msgid "Edit profile"
msgstr "Import-Profil bearbeiten"
#: templates/includes/navbar/user_menu.html:46
msgid "Clear cache"
msgstr "Cache leeren"
#: templates/includes/navbar/user_menu.html:45
#: templates/includes/navbar/user_menu.html:50
msgid "Logout"
msgstr "Abmelden"
#: templates/includes/scripts/hyperscript/htmx_error_handler.html:8
#, fuzzy
msgid "Access Denied"
msgstr ""
msgstr "Access Denied"
#: templates/includes/scripts/hyperscript/htmx_error_handler.html:9
#, fuzzy
msgid ""
"You do not have permission to perform this action or access this resource."
msgstr ""
"You do not have permission to perform this action or access this resource."
#: templates/includes/scripts/hyperscript/htmx_error_handler.html:18
msgid "Something went wrong loading your data"
@@ -2583,18 +2692,25 @@ msgstr "Einnahmen/Ausgaben nach Konto"
msgid "Income/Expense by Currency"
msgstr "Einnahmen/Ausgaben nach Währung"
#: templates/insights/fragments/category_overview/index.html:12
#: templates/insights/fragments/category_overview/index.html:13
#: templates/monthly_overview/fragments/monthly_summary.html:167
msgid "Total"
msgstr "Gesamt"
#: templates/insights/fragments/category_overview/index.html:202
#, fuzzy
#| msgid "final total"
msgid "Final Total"
msgstr "Gesamtbilanz"
#: templates/insights/fragments/emergency_fund.html:15
msgid "You've spent an average of"
msgstr "Deine Ausgaben liegen im Durchschnitt bei"
#: templates/insights/fragments/emergency_fund.html:23
#, fuzzy
msgid "on the last 12 months, at this rate you could go by"
msgstr ""
msgstr "on the last 12 months, at this rate you could go by"
#: templates/insights/fragments/emergency_fund.html:25
msgid "months without any income."
@@ -2720,12 +2836,14 @@ msgid "No installment plans"
msgstr "Keine Ratenzahlungs-Pläne"
#: templates/layouts/base.html:40
#, fuzzy
msgid "This is a demo!"
msgstr ""
msgstr "This is a demo!"
#: templates/layouts/base.html:40
#, fuzzy
msgid "Any data you add here will be wiped in 24hrs or less"
msgstr ""
msgstr "Any data you add here will be wiped in 24hrs or less"
#: templates/mini_tools/currency_converter/currency_converter.html:58
msgid "Invert"
@@ -3026,6 +3144,38 @@ msgstr "Gelöschte Transaktionen"
msgid "Unchanged"
msgstr "Unverändert"
#: templates/users/fragments/add.html:5
#, fuzzy
#| msgid "Add new"
msgid "Add user"
msgstr "Neue hinzufügen"
#: templates/users/fragments/edit.html:5
#, fuzzy
#| msgid "Edit category"
msgid "Edit user"
msgstr "Kategorie bearbeiten"
#: templates/users/fragments/list.html:30
#, fuzzy
#| msgid "E-mail"
msgid "Email"
msgstr "E-Mail"
#: templates/users/fragments/list.html:31
msgid "Superuser"
msgstr ""
#: templates/users/fragments/list.html:51
msgid "Impersonate"
msgstr ""
#: templates/users/fragments/list.html:80
#, fuzzy
#| msgid "Users"
msgid "No users"
msgstr "Nutzer"
#: templates/users/generic/hide_amounts.html:2
msgid "Hide amounts"
msgstr "Werte ausblenden"
@@ -3043,12 +3193,14 @@ msgid "Show amounts"
msgstr "Werte einblenden"
#: templates/users/login.html:17
#, fuzzy
msgid "Welcome to WYGIWYH's demo!"
msgstr ""
msgstr "Welcome to WYGIWYH's demo!"
#: templates/users/login.html:18
#, fuzzy
msgid "Use the credentials below to login"
msgstr ""
msgstr "Use the credentials below to login"
#: templates/yearly_overview/pages/overview_by_account.html:7
#: templates/yearly_overview/pages/overview_by_currency.html:9

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-03-31 05:30+0000\n"
"POT-Creation-Date: 2025-04-13 22:01+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -30,6 +30,7 @@ msgstr ""
#: apps/transactions/forms.py:269 apps/transactions/forms.py:629
#: apps/transactions/forms.py:672 apps/transactions/forms.py:704
#: apps/transactions/forms.py:739 apps/transactions/forms.py:891
#: apps/users/forms.py:210 apps/users/forms.py:372
msgid "Update"
msgstr ""
@@ -41,8 +42,8 @@ msgstr ""
#: apps/transactions/forms.py:187 apps/transactions/forms.py:211
#: apps/transactions/forms.py:637 apps/transactions/forms.py:680
#: apps/transactions/forms.py:712 apps/transactions/forms.py:747
#: apps/transactions/forms.py:899
#: templates/account_groups/fragments/list.html:9
#: apps/transactions/forms.py:899 apps/users/forms.py:218
#: apps/users/forms.py:380 templates/account_groups/fragments/list.html:9
#: templates/accounts/fragments/list.html:9
#: templates/categories/fragments/list.html:9
#: templates/currencies/fragments/list.html:9
@@ -57,6 +58,7 @@ msgstr ""
#: templates/mini_tools/unit_price_calculator.html:162
#: templates/recurring_transactions/fragments/list.html:9
#: templates/rules/fragments/list.html:9 templates/tags/fragments/list.html:9
#: templates/users/fragments/list.html:10
msgid "Add"
msgstr ""
@@ -74,8 +76,9 @@ msgstr ""
#: apps/transactions/forms.py:40 apps/transactions/forms.py:303
#: apps/transactions/forms.py:310 apps/transactions/forms.py:510
#: apps/transactions/forms.py:771 apps/transactions/models.py:305
#: apps/transactions/models.py:488 apps/transactions/models.py:686
#: templates/insights/fragments/category_overview/index.html:9
#: apps/transactions/models.py:488 apps/transactions/models.py:688
#: templates/insights/fragments/category_overview/index.html:10
#: templates/insights/fragments/category_overview/index.html:215
msgid "Category"
msgstr ""
@@ -86,7 +89,7 @@ msgstr ""
#: apps/transactions/forms.py:48 apps/transactions/forms.py:319
#: apps/transactions/forms.py:327 apps/transactions/forms.py:503
#: apps/transactions/forms.py:764 apps/transactions/models.py:311
#: apps/transactions/models.py:490 apps/transactions/models.py:690
#: apps/transactions/models.py:490 apps/transactions/models.py:692
#: templates/includes/navbar.html:108 templates/tags/fragments/list.html:5
#: templates/tags/pages/index.html:4
msgid "Tags"
@@ -107,6 +110,7 @@ msgstr ""
#: templates/recurring_transactions/fragments/table.html:18
#: templates/rules/fragments/list.html:26
#: templates/tags/fragments/table.html:16
#: templates/users/fragments/list.html:29
msgid "Name"
msgstr ""
@@ -158,7 +162,7 @@ msgstr ""
#: apps/rules/models.py:30 apps/rules/models.py:242
#: apps/transactions/forms.py:60 apps/transactions/forms.py:495
#: apps/transactions/forms.py:756 apps/transactions/models.py:278
#: apps/transactions/models.py:448 apps/transactions/models.py:668
#: apps/transactions/models.py:448 apps/transactions/models.py:670
msgid "Account"
msgstr ""
@@ -280,7 +284,11 @@ msgstr ""
msgid "Ungrouped"
msgstr ""
#: apps/common/fields/month_year.py:23 apps/common/fields/month_year.py:51
#: apps/common/fields/month_year.py:30
msgid "Invalid date format. Use YYYY-MM or YYYY-MM-DD."
msgstr ""
#: apps/common/fields/month_year.py:59
msgid "Invalid date format. Use YYYY-MM."
msgstr ""
@@ -312,7 +320,7 @@ msgid ""
"owner.<br/>Public: Shown for all users. Only editable by the owner."
msgstr ""
#: apps/common/forms.py:79 apps/users/forms.py:131
#: apps/common/forms.py:79 apps/users/forms.py:135
msgid "Save"
msgstr ""
@@ -403,7 +411,7 @@ msgstr ""
msgid "Info"
msgstr ""
#: apps/common/views.py:110
#: apps/common/views.py:111
msgid "Cache cleared successfully"
msgstr ""
@@ -528,7 +536,7 @@ msgstr ""
#: templates/categories/fragments/list.html:21
#: templates/entities/fragments/list.html:21
#: templates/recurring_transactions/fragments/list.html:21
#: templates/tags/fragments/list.html:21
#: templates/tags/fragments/list.html:21 templates/users/fragments/list.html:28
msgid "Active"
msgstr ""
@@ -693,7 +701,7 @@ msgstr ""
#: apps/dca/models.py:26 apps/dca/models.py:181 apps/rules/forms.py:173
#: apps/rules/forms.py:188 apps/rules/models.py:37 apps/rules/models.py:270
#: apps/transactions/forms.py:347 apps/transactions/models.py:301
#: apps/transactions/models.py:497 apps/transactions/models.py:696
#: apps/transactions/models.py:497 apps/transactions/models.py:698
msgid "Notes"
msgstr ""
@@ -750,6 +758,8 @@ msgid "Entry deleted successfully"
msgstr ""
#: apps/export_app/forms.py:14 apps/export_app/forms.py:131
#: templates/includes/navbar.html:147 templates/users/fragments/list.html:6
#: templates/users/pages/index.html:4
msgid "Users"
msgstr ""
@@ -774,13 +784,13 @@ msgstr ""
#: apps/transactions/forms.py:56 apps/transactions/forms.py:518
#: apps/transactions/forms.py:779 apps/transactions/models.py:261
#: apps/transactions/models.py:316 apps/transactions/models.py:493
#: apps/transactions/models.py:693 templates/entities/fragments/list.html:5
#: apps/transactions/models.py:695 templates/entities/fragments/list.html:5
#: templates/entities/pages/index.html:4 templates/includes/navbar.html:110
msgid "Entities"
msgstr ""
#: apps/export_app/forms.py:56 apps/export_app/forms.py:140
#: apps/transactions/models.py:730 templates/includes/navbar.html:74
#: apps/transactions/models.py:732 templates/includes/navbar.html:74
#: templates/recurring_transactions/fragments/list.html:5
#: templates/recurring_transactions/pages/index.html:4
msgid "Recurring Transactions"
@@ -940,7 +950,9 @@ msgstr ""
#: apps/insights/forms.py:119 apps/insights/utils/sankey.py:36
#: apps/insights/utils/sankey.py:167
#: templates/insights/fragments/category_overview/index.html:18
#: templates/insights/fragments/category_overview/index.html:19
#: templates/insights/fragments/category_overview/index.html:87
#: templates/insights/fragments/category_overview/index.html:116
msgid "Uncategorized"
msgstr ""
@@ -1023,7 +1035,7 @@ msgstr ""
#: apps/rules/forms.py:167 apps/rules/forms.py:180 apps/rules/models.py:31
#: apps/rules/models.py:246 apps/transactions/models.py:285
#: apps/transactions/models.py:453 apps/transactions/models.py:674
#: apps/transactions/models.py:453 apps/transactions/models.py:676
msgid "Type"
msgstr ""
@@ -1040,20 +1052,20 @@ msgstr ""
#: apps/rules/models.py:258 apps/transactions/forms.py:68
#: apps/transactions/forms.py:334 apps/transactions/forms.py:524
#: apps/transactions/models.py:289 apps/transactions/models.py:471
#: apps/transactions/models.py:698
#: apps/transactions/models.py:700
msgid "Reference Date"
msgstr ""
#: apps/rules/forms.py:171 apps/rules/forms.py:184 apps/rules/models.py:35
#: apps/rules/models.py:262 apps/transactions/models.py:294
#: apps/transactions/models.py:679 templates/insights/fragments/sankey.html:95
#: apps/transactions/models.py:681 templates/insights/fragments/sankey.html:95
msgid "Amount"
msgstr ""
#: apps/rules/forms.py:172 apps/rules/forms.py:185 apps/rules/models.py:14
#: apps/rules/models.py:36 apps/rules/models.py:266
#: apps/transactions/forms.py:338 apps/transactions/models.py:299
#: apps/transactions/models.py:455 apps/transactions/models.py:682
#: apps/transactions/models.py:455 apps/transactions/models.py:684
msgid "Description"
msgstr ""
@@ -1304,7 +1316,7 @@ msgstr ""
#: templates/calendar_view/fragments/list.html:52
#: templates/calendar_view/fragments/list.html:54
#: templates/cotton/ui/quick_transactions_buttons.html:10
#: templates/insights/fragments/category_overview/index.html:10
#: templates/insights/fragments/category_overview/index.html:11
#: templates/monthly_overview/fragments/monthly_summary.html:39
msgid "Income"
msgstr ""
@@ -1315,7 +1327,7 @@ msgstr ""
#: templates/calendar_view/fragments/list.html:56
#: templates/calendar_view/fragments/list.html:58
#: templates/cotton/ui/quick_transactions_buttons.html:18
#: templates/insights/fragments/category_overview/index.html:11
#: templates/insights/fragments/category_overview/index.html:12
msgid "Expense"
msgstr ""
@@ -1323,7 +1335,7 @@ msgstr ""
msgid "Installment Plan"
msgstr ""
#: apps/transactions/models.py:336 apps/transactions/models.py:729
#: apps/transactions/models.py:336 apps/transactions/models.py:731
msgid "Recurring Transaction"
msgstr ""
@@ -1380,11 +1392,11 @@ msgstr ""
msgid "The installment number to start counting from"
msgstr ""
#: apps/transactions/models.py:469 apps/transactions/models.py:702
#: apps/transactions/models.py:469 apps/transactions/models.py:704
msgid "Start Date"
msgstr ""
#: apps/transactions/models.py:473 apps/transactions/models.py:703
#: apps/transactions/models.py:473 apps/transactions/models.py:705
msgid "End Date"
msgstr ""
@@ -1396,48 +1408,48 @@ msgstr ""
msgid "Installment Amount"
msgstr ""
#: apps/transactions/models.py:500 apps/transactions/models.py:719
#: apps/transactions/models.py:500 apps/transactions/models.py:721
msgid "Add description to transactions"
msgstr ""
#: apps/transactions/models.py:503 apps/transactions/models.py:722
#: apps/transactions/models.py:503 apps/transactions/models.py:724
msgid "Add notes to transactions"
msgstr ""
#: apps/transactions/models.py:661
#: apps/transactions/models.py:663
msgid "day(s)"
msgstr ""
#: apps/transactions/models.py:662
#: apps/transactions/models.py:664
msgid "week(s)"
msgstr ""
#: apps/transactions/models.py:663
#: apps/transactions/models.py:665
msgid "month(s)"
msgstr ""
#: apps/transactions/models.py:664
#: apps/transactions/models.py:666
msgid "year(s)"
msgstr ""
#: apps/transactions/models.py:666
#: apps/transactions/models.py:668
#: templates/recurring_transactions/fragments/list.html:24
msgid "Paused"
msgstr ""
#: apps/transactions/models.py:705
#: apps/transactions/models.py:707
msgid "Recurrence Type"
msgstr ""
#: apps/transactions/models.py:708
#: apps/transactions/models.py:710
msgid "Recurrence Interval"
msgstr ""
#: apps/transactions/models.py:712
#: apps/transactions/models.py:714
msgid "Last Generated Date"
msgstr ""
#: apps/transactions/models.py:715
#: apps/transactions/models.py:717
msgid "Last Generated Reference Date"
msgstr ""
@@ -1614,40 +1626,104 @@ msgstr ""
msgid "Important dates"
msgstr ""
#: apps/users/forms.py:19 apps/users/models.py:13 templates/users/login.html:19
#: apps/users/forms.py:23 apps/users/models.py:13 templates/users/login.html:19
msgid "E-mail"
msgstr ""
#: apps/users/forms.py:25 templates/users/login.html:20
#: apps/users/forms.py:29 templates/users/login.html:20
msgid "Password"
msgstr ""
#: apps/users/forms.py:33
#: apps/users/forms.py:37
msgid "Invalid e-mail or password"
msgstr ""
#: apps/users/forms.py:34
#: apps/users/forms.py:38
msgid "This account is deactivated"
msgstr ""
#: apps/users/forms.py:50 apps/users/forms.py:63 apps/users/forms.py:85
#: apps/users/forms.py:54 apps/users/forms.py:67 apps/users/forms.py:89
#: templates/monthly_overview/pages/overview.html:153
#: templates/transactions/pages/transactions.html:35
msgid "Default"
msgstr ""
#: apps/users/forms.py:91 apps/users/models.py:41
#: apps/users/forms.py:95 apps/users/models.py:41
msgid "Date Format"
msgstr ""
#: apps/users/forms.py:96 apps/users/models.py:46
#: apps/users/forms.py:100 apps/users/models.py:46
msgid "Datetime Format"
msgstr ""
#: apps/users/forms.py:102 apps/users/models.py:49
#: apps/users/forms.py:106 apps/users/models.py:49
msgid "Number Format"
msgstr ""
#: apps/users/forms.py:141
#, python-format
msgid ""
"This changes the language (if available) and how numbers and dates are "
"displayed\n"
"Consider helping translate WYGIWYH to your language at %(translation_link)s"
msgstr ""
#: apps/users/forms.py:150
msgid "New Password"
msgstr ""
#: apps/users/forms.py:153
msgid "Leave blank to keep the current password."
msgstr ""
#: apps/users/forms.py:156
msgid "Confirm New Password"
msgstr ""
#: apps/users/forms.py:168 apps/users/forms.py:329
msgid ""
"Designates whether this user should be treated as active. Unselect this "
"instead of deleting accounts."
msgstr ""
#: apps/users/forms.py:171 apps/users/forms.py:332
msgid ""
"Designates that this user has all permissions without explicitly assigning "
"them."
msgstr ""
#: apps/users/forms.py:242
msgid "This email address is already in use by another account."
msgstr ""
#: apps/users/forms.py:250
msgid "The two password fields didn't match."
msgstr ""
#: apps/users/forms.py:252
msgid "Please confirm your new password."
msgstr ""
#: apps/users/forms.py:254
msgid "Please enter the new password first."
msgstr ""
#: apps/users/forms.py:274
msgid "You cannot deactivate your own account using this form."
msgstr ""
#: apps/users/forms.py:287
msgid "Cannot remove status from the last superuser."
msgstr ""
#: apps/users/forms.py:293
msgid "You cannot remove your own superuser status using this form."
msgstr ""
#: apps/users/forms.py:390
msgid "A user with this email address already exists."
msgstr ""
#: apps/users/models.py:27 templates/includes/navbar.html:28
msgid "Yearly by currency"
msgstr ""
@@ -1688,26 +1764,34 @@ msgstr ""
msgid "Start page"
msgstr ""
#: apps/users/views.py:62
#: apps/users/views.py:67
msgid "Transaction amounts are now hidden"
msgstr ""
#: apps/users/views.py:65
#: apps/users/views.py:70
msgid "Transaction amounts are now displayed"
msgstr ""
#: apps/users/views.py:83
#: apps/users/views.py:88
msgid "Sounds are now muted"
msgstr ""
#: apps/users/views.py:86
#: apps/users/views.py:91
msgid "Sounds will now play"
msgstr ""
#: apps/users/views.py:102
#: apps/users/views.py:107
msgid "Your settings have been updated"
msgstr ""
#: apps/users/views.py:151
msgid "Item added successfully"
msgstr ""
#: apps/users/views.py:182
msgid "Item updated successfully"
msgstr ""
#: templates/account_groups/fragments/add.html:5
msgid "Add account group"
msgstr ""
@@ -1730,6 +1814,7 @@ msgstr ""
#: templates/recurring_transactions/fragments/table.html:25
#: templates/rules/fragments/list.html:33
#: templates/tags/fragments/table.html:23
#: templates/users/fragments/list.html:38
msgid "Actions"
msgstr ""
@@ -1752,6 +1837,7 @@ msgstr ""
#: templates/rules/fragments/transaction_rule/view.html:47
#: templates/rules/fragments/transaction_rule/view.html:80
#: templates/tags/fragments/table.html:28
#: templates/users/fragments/list.html:43
msgid "Edit"
msgstr ""
@@ -1974,7 +2060,7 @@ msgid "Muted"
msgstr ""
#: templates/categories/fragments/table.html:75
#: templates/insights/fragments/category_overview/index.html:67
#: templates/insights/fragments/category_overview/index.html:225
msgid "No categories"
msgstr ""
@@ -2472,15 +2558,19 @@ msgstr ""
msgid "Automation"
msgstr ""
#: templates/includes/navbar.html:150
#: templates/includes/navbar.html:145
msgid "Admin"
msgstr ""
#: templates/includes/navbar.html:154
msgid "Only use this if you know what you're doing"
msgstr ""
#: templates/includes/navbar.html:151
#: templates/includes/navbar.html:155
msgid "Django Admin"
msgstr ""
#: templates/includes/navbar.html:160
#: templates/includes/navbar.html:165
msgid "Calculator"
msgstr ""
@@ -2488,11 +2578,15 @@ msgstr ""
msgid "Settings"
msgstr ""
#: templates/includes/navbar/user_menu.html:41
#: templates/includes/navbar/user_menu.html:19
msgid "Edit profile"
msgstr ""
#: templates/includes/navbar/user_menu.html:46
msgid "Clear cache"
msgstr ""
#: templates/includes/navbar/user_menu.html:45
#: templates/includes/navbar/user_menu.html:50
msgid "Logout"
msgstr ""
@@ -2542,11 +2636,15 @@ msgstr ""
msgid "Income/Expense by Currency"
msgstr ""
#: templates/insights/fragments/category_overview/index.html:12
#: templates/insights/fragments/category_overview/index.html:13
#: templates/monthly_overview/fragments/monthly_summary.html:167
msgid "Total"
msgstr ""
#: templates/insights/fragments/category_overview/index.html:202
msgid "Final Total"
msgstr ""
#: templates/insights/fragments/emergency_fund.html:15
msgid "You've spent an average of"
msgstr ""
@@ -2978,6 +3076,30 @@ msgstr ""
msgid "Unchanged"
msgstr ""
#: templates/users/fragments/add.html:5
msgid "Add user"
msgstr ""
#: templates/users/fragments/edit.html:5
msgid "Edit user"
msgstr ""
#: templates/users/fragments/list.html:30
msgid "Email"
msgstr ""
#: templates/users/fragments/list.html:31
msgid "Superuser"
msgstr ""
#: templates/users/fragments/list.html:51
msgid "Impersonate"
msgstr ""
#: templates/users/fragments/list.html:80
msgid "No users"
msgstr ""
#: templates/users/generic/hide_amounts.html:2
msgid "Hide amounts"
msgstr ""

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-03-31 05:30+0000\n"
"PO-Revision-Date: 2025-03-13 07:05+0000\n"
"POT-Creation-Date: 2025-04-13 22:01+0000\n"
"PO-Revision-Date: 2025-04-13 11:16+0000\n"
"Last-Translator: Dimitri Decrock <dj.flashpower@gmail.com>\n"
"Language-Team: Dutch <https://translations.herculino.com/projects/wygiwyh/"
"app/nl/>\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.10.2\n"
"X-Generator: Weblate 5.10.4\n"
#: apps/accounts/forms.py:24
msgid "Group name"
@@ -31,6 +31,7 @@ msgstr "Groepsnaam"
#: apps/transactions/forms.py:269 apps/transactions/forms.py:629
#: apps/transactions/forms.py:672 apps/transactions/forms.py:704
#: apps/transactions/forms.py:739 apps/transactions/forms.py:891
#: apps/users/forms.py:210 apps/users/forms.py:372
msgid "Update"
msgstr "Bijwerken"
@@ -42,8 +43,8 @@ msgstr "Bijwerken"
#: apps/transactions/forms.py:187 apps/transactions/forms.py:211
#: apps/transactions/forms.py:637 apps/transactions/forms.py:680
#: apps/transactions/forms.py:712 apps/transactions/forms.py:747
#: apps/transactions/forms.py:899
#: templates/account_groups/fragments/list.html:9
#: apps/transactions/forms.py:899 apps/users/forms.py:218
#: apps/users/forms.py:380 templates/account_groups/fragments/list.html:9
#: templates/accounts/fragments/list.html:9
#: templates/categories/fragments/list.html:9
#: templates/currencies/fragments/list.html:9
@@ -58,6 +59,7 @@ msgstr "Bijwerken"
#: templates/mini_tools/unit_price_calculator.html:162
#: templates/recurring_transactions/fragments/list.html:9
#: templates/rules/fragments/list.html:9 templates/tags/fragments/list.html:9
#: templates/users/fragments/list.html:10
msgid "Add"
msgstr "Toevoegen"
@@ -75,8 +77,9 @@ msgstr "Nieuw saldo"
#: apps/transactions/forms.py:40 apps/transactions/forms.py:303
#: apps/transactions/forms.py:310 apps/transactions/forms.py:510
#: apps/transactions/forms.py:771 apps/transactions/models.py:305
#: apps/transactions/models.py:488 apps/transactions/models.py:686
#: templates/insights/fragments/category_overview/index.html:9
#: apps/transactions/models.py:488 apps/transactions/models.py:688
#: templates/insights/fragments/category_overview/index.html:10
#: templates/insights/fragments/category_overview/index.html:215
msgid "Category"
msgstr "Categorie"
@@ -87,7 +90,7 @@ msgstr "Categorie"
#: apps/transactions/forms.py:48 apps/transactions/forms.py:319
#: apps/transactions/forms.py:327 apps/transactions/forms.py:503
#: apps/transactions/forms.py:764 apps/transactions/models.py:311
#: apps/transactions/models.py:490 apps/transactions/models.py:690
#: apps/transactions/models.py:490 apps/transactions/models.py:692
#: templates/includes/navbar.html:108 templates/tags/fragments/list.html:5
#: templates/tags/pages/index.html:4
msgid "Tags"
@@ -108,6 +111,7 @@ msgstr "Labels"
#: templates/recurring_transactions/fragments/table.html:18
#: templates/rules/fragments/list.html:26
#: templates/tags/fragments/table.html:16
#: templates/users/fragments/list.html:29
msgid "Name"
msgstr "Naam"
@@ -163,7 +167,7 @@ msgstr ""
#: apps/rules/models.py:30 apps/rules/models.py:242
#: apps/transactions/forms.py:60 apps/transactions/forms.py:495
#: apps/transactions/forms.py:756 apps/transactions/models.py:278
#: apps/transactions/models.py:448 apps/transactions/models.py:668
#: apps/transactions/models.py:448 apps/transactions/models.py:670
msgid "Account"
msgstr "Rekening"
@@ -286,7 +290,11 @@ msgstr "Fout bij het aanmaken van een nieuwe instantie"
msgid "Ungrouped"
msgstr "Niet gegroepeerd"
#: apps/common/fields/month_year.py:23 apps/common/fields/month_year.py:51
#: apps/common/fields/month_year.py:30
msgid "Invalid date format. Use YYYY-MM or YYYY-MM-DD."
msgstr "Ongeldige datumnotatie. Gebruik JJJJ-MM of JJJJ-MM-DD."
#: apps/common/fields/month_year.py:59
msgid "Invalid date format. Use YYYY-MM."
msgstr "Ongeldige datumnotatie. Gebruik JJJJ-MM."
@@ -323,7 +331,7 @@ msgstr ""
"bewerkbaar door de eigenaar.<br/>Publiek: Weergegeven voor alle gebruikers. "
"Alleen bewerkbaar door de eigenaar."
#: apps/common/forms.py:79 apps/users/forms.py:131
#: apps/common/forms.py:79 apps/users/forms.py:135
msgid "Save"
msgstr "Opslaan"
@@ -414,7 +422,7 @@ msgstr "Fout"
msgid "Info"
msgstr "Info"
#: apps/common/views.py:110
#: apps/common/views.py:111
msgid "Cache cleared successfully"
msgstr "Cache succesvol gewist"
@@ -539,7 +547,7 @@ msgstr "Soort Dienst"
#: templates/categories/fragments/list.html:21
#: templates/entities/fragments/list.html:21
#: templates/recurring_transactions/fragments/list.html:21
#: templates/tags/fragments/list.html:21
#: templates/tags/fragments/list.html:21 templates/users/fragments/list.html:28
msgid "Active"
msgstr "Actief"
@@ -714,7 +722,7 @@ msgstr "Betaal Munteenheid"
#: apps/dca/models.py:26 apps/dca/models.py:181 apps/rules/forms.py:173
#: apps/rules/forms.py:188 apps/rules/models.py:37 apps/rules/models.py:270
#: apps/transactions/forms.py:347 apps/transactions/models.py:301
#: apps/transactions/models.py:497 apps/transactions/models.py:696
#: apps/transactions/models.py:497 apps/transactions/models.py:698
msgid "Notes"
msgstr "Opmerkingen"
@@ -771,6 +779,8 @@ msgid "Entry deleted successfully"
msgstr "Item succesvol verwijderd"
#: apps/export_app/forms.py:14 apps/export_app/forms.py:131
#: templates/includes/navbar.html:147 templates/users/fragments/list.html:6
#: templates/users/pages/index.html:4
msgid "Users"
msgstr "Gebruikers"
@@ -795,13 +805,13 @@ msgstr "Categorieën"
#: apps/transactions/forms.py:56 apps/transactions/forms.py:518
#: apps/transactions/forms.py:779 apps/transactions/models.py:261
#: apps/transactions/models.py:316 apps/transactions/models.py:493
#: apps/transactions/models.py:693 templates/entities/fragments/list.html:5
#: apps/transactions/models.py:695 templates/entities/fragments/list.html:5
#: templates/entities/pages/index.html:4 templates/includes/navbar.html:110
msgid "Entities"
msgstr "Bedrijven"
#: apps/export_app/forms.py:56 apps/export_app/forms.py:140
#: apps/transactions/models.py:730 templates/includes/navbar.html:74
#: apps/transactions/models.py:732 templates/includes/navbar.html:74
#: templates/recurring_transactions/fragments/list.html:5
#: templates/recurring_transactions/pages/index.html:4
msgid "Recurring Transactions"
@@ -963,7 +973,9 @@ msgstr "Run met succes verwijderd"
#: apps/insights/forms.py:119 apps/insights/utils/sankey.py:36
#: apps/insights/utils/sankey.py:167
#: templates/insights/fragments/category_overview/index.html:18
#: templates/insights/fragments/category_overview/index.html:19
#: templates/insights/fragments/category_overview/index.html:87
#: templates/insights/fragments/category_overview/index.html:116
msgid "Uncategorized"
msgstr "Ongecategoriseerd"
@@ -1046,7 +1058,7 @@ msgstr "Operator"
#: apps/rules/forms.py:167 apps/rules/forms.py:180 apps/rules/models.py:31
#: apps/rules/models.py:246 apps/transactions/models.py:285
#: apps/transactions/models.py:453 apps/transactions/models.py:674
#: apps/transactions/models.py:453 apps/transactions/models.py:676
msgid "Type"
msgstr "Soort"
@@ -1063,20 +1075,20 @@ msgstr "Betaald"
#: apps/rules/models.py:258 apps/transactions/forms.py:68
#: apps/transactions/forms.py:334 apps/transactions/forms.py:524
#: apps/transactions/models.py:289 apps/transactions/models.py:471
#: apps/transactions/models.py:698
#: apps/transactions/models.py:700
msgid "Reference Date"
msgstr "Referentiedatum"
#: apps/rules/forms.py:171 apps/rules/forms.py:184 apps/rules/models.py:35
#: apps/rules/models.py:262 apps/transactions/models.py:294
#: apps/transactions/models.py:679 templates/insights/fragments/sankey.html:95
#: apps/transactions/models.py:681 templates/insights/fragments/sankey.html:95
msgid "Amount"
msgstr "Bedrag"
#: apps/rules/forms.py:172 apps/rules/forms.py:185 apps/rules/models.py:14
#: apps/rules/models.py:36 apps/rules/models.py:266
#: apps/transactions/forms.py:338 apps/transactions/models.py:299
#: apps/transactions/models.py:455 apps/transactions/models.py:682
#: apps/transactions/models.py:455 apps/transactions/models.py:684
msgid "Description"
msgstr "Beschrijving"
@@ -1335,7 +1347,7 @@ msgstr "Bedrijf"
#: templates/calendar_view/fragments/list.html:52
#: templates/calendar_view/fragments/list.html:54
#: templates/cotton/ui/quick_transactions_buttons.html:10
#: templates/insights/fragments/category_overview/index.html:10
#: templates/insights/fragments/category_overview/index.html:11
#: templates/monthly_overview/fragments/monthly_summary.html:39
msgid "Income"
msgstr "Ontvangsten Transactie"
@@ -1346,7 +1358,7 @@ msgstr "Ontvangsten Transactie"
#: templates/calendar_view/fragments/list.html:56
#: templates/calendar_view/fragments/list.html:58
#: templates/cotton/ui/quick_transactions_buttons.html:18
#: templates/insights/fragments/category_overview/index.html:11
#: templates/insights/fragments/category_overview/index.html:12
msgid "Expense"
msgstr "Uitgave"
@@ -1354,7 +1366,7 @@ msgstr "Uitgave"
msgid "Installment Plan"
msgstr "Afbetalingsplan"
#: apps/transactions/models.py:336 apps/transactions/models.py:729
#: apps/transactions/models.py:336 apps/transactions/models.py:731
msgid "Recurring Transaction"
msgstr "Terugkerende verrichting"
@@ -1411,11 +1423,11 @@ msgstr "Begin afbetaling"
msgid "The installment number to start counting from"
msgstr "Het nummer van de aflevering om mee te beginnen"
#: apps/transactions/models.py:469 apps/transactions/models.py:702
#: apps/transactions/models.py:469 apps/transactions/models.py:704
msgid "Start Date"
msgstr "Startdatum"
#: apps/transactions/models.py:473 apps/transactions/models.py:703
#: apps/transactions/models.py:473 apps/transactions/models.py:705
msgid "End Date"
msgstr "Einddatum"
@@ -1427,48 +1439,48 @@ msgstr "Terugkeerpatroon"
msgid "Installment Amount"
msgstr "Termijnbedrag"
#: apps/transactions/models.py:500 apps/transactions/models.py:719
#: apps/transactions/models.py:500 apps/transactions/models.py:721
msgid "Add description to transactions"
msgstr "Beschrijving toevoegen aan verrichting"
#: apps/transactions/models.py:503 apps/transactions/models.py:722
#: apps/transactions/models.py:503 apps/transactions/models.py:724
msgid "Add notes to transactions"
msgstr "Notities toevoegen aan verrichting"
#: apps/transactions/models.py:661
#: apps/transactions/models.py:663
msgid "day(s)"
msgstr "dag(en)"
#: apps/transactions/models.py:662
#: apps/transactions/models.py:664
msgid "week(s)"
msgstr "we(e)k(en)"
#: apps/transactions/models.py:663
#: apps/transactions/models.py:665
msgid "month(s)"
msgstr "maand(en)"
#: apps/transactions/models.py:664
#: apps/transactions/models.py:666
msgid "year(s)"
msgstr "ja(a)r(en)"
#: apps/transactions/models.py:666
#: apps/transactions/models.py:668
#: templates/recurring_transactions/fragments/list.html:24
msgid "Paused"
msgstr "Gepauzeerd"
#: apps/transactions/models.py:705
#: apps/transactions/models.py:707
msgid "Recurrence Type"
msgstr "Type Terugkeerpatroon"
#: apps/transactions/models.py:708
#: apps/transactions/models.py:710
msgid "Recurrence Interval"
msgstr "Terugkeer Interval"
#: apps/transactions/models.py:712
#: apps/transactions/models.py:714
msgid "Last Generated Date"
msgstr "Laatste Gegenereerde Datum"
#: apps/transactions/models.py:715
#: apps/transactions/models.py:717
msgid "Last Generated Reference Date"
msgstr "Laatste Gegenereerde Referentiedatum"
@@ -1645,40 +1657,111 @@ msgstr "Rechten"
msgid "Important dates"
msgstr "Belangrijke datums"
#: apps/users/forms.py:19 apps/users/models.py:13 templates/users/login.html:19
#: apps/users/forms.py:23 apps/users/models.py:13 templates/users/login.html:19
msgid "E-mail"
msgstr "E-mailadres"
#: apps/users/forms.py:25 templates/users/login.html:20
#: apps/users/forms.py:29 templates/users/login.html:20
msgid "Password"
msgstr "Wachtwoord"
#: apps/users/forms.py:33
#: apps/users/forms.py:37
msgid "Invalid e-mail or password"
msgstr "Ongeldig e-mailadres of wachtwoord"
#: apps/users/forms.py:34
#: apps/users/forms.py:38
msgid "This account is deactivated"
msgstr "Deze gebruiker is gedeactiveerd"
#: apps/users/forms.py:50 apps/users/forms.py:63 apps/users/forms.py:85
#: apps/users/forms.py:54 apps/users/forms.py:67 apps/users/forms.py:89
#: templates/monthly_overview/pages/overview.html:153
#: templates/transactions/pages/transactions.html:35
msgid "Default"
msgstr "Standaard"
#: apps/users/forms.py:91 apps/users/models.py:41
#: apps/users/forms.py:95 apps/users/models.py:41
msgid "Date Format"
msgstr "Datumnotatie"
#: apps/users/forms.py:96 apps/users/models.py:46
#: apps/users/forms.py:100 apps/users/models.py:46
msgid "Datetime Format"
msgstr "Tijdsnotatie"
#: apps/users/forms.py:102 apps/users/models.py:49
#: apps/users/forms.py:106 apps/users/models.py:49
msgid "Number Format"
msgstr "Schrijfwijze Nummers"
#: apps/users/forms.py:141
#, python-format
msgid ""
"This changes the language (if available) and how numbers and dates are "
"displayed\n"
"Consider helping translate WYGIWYH to your language at %(translation_link)s"
msgstr ""
"Dit verandert de taal (indien beschikbaar) en hoe getallen en datums worden "
"weergegeven\n"
"Overweeg om WYGIWYH te helpen vertalen naar jouw taal op %(translation_link)s"
#: apps/users/forms.py:150
#, fuzzy
#| msgid "Password"
msgid "New Password"
msgstr "Wachtwoord"
#: apps/users/forms.py:153
msgid "Leave blank to keep the current password."
msgstr ""
#: apps/users/forms.py:156
msgid "Confirm New Password"
msgstr ""
#: apps/users/forms.py:168 apps/users/forms.py:329
msgid ""
"Designates whether this user should be treated as active. Unselect this "
"instead of deleting accounts."
msgstr ""
#: apps/users/forms.py:171 apps/users/forms.py:332
msgid ""
"Designates that this user has all permissions without explicitly assigning "
"them."
msgstr ""
#: apps/users/forms.py:242
msgid "This email address is already in use by another account."
msgstr ""
#: apps/users/forms.py:250
msgid "The two password fields didn't match."
msgstr ""
#: apps/users/forms.py:252
msgid "Please confirm your new password."
msgstr ""
#: apps/users/forms.py:254
msgid "Please enter the new password first."
msgstr ""
#: apps/users/forms.py:274
msgid "You cannot deactivate your own account using this form."
msgstr ""
#: apps/users/forms.py:287
msgid "Cannot remove status from the last superuser."
msgstr ""
#: apps/users/forms.py:293
msgid "You cannot remove your own superuser status using this form."
msgstr ""
#: apps/users/forms.py:390
#, fuzzy
#| msgid "A value for this field already exists in the rule."
msgid "A user with this email address already exists."
msgstr "Een waarde voor dit veld bestaat al in de regel."
#: apps/users/models.py:27 templates/includes/navbar.html:28
msgid "Yearly by currency"
msgstr "Jaarlijks per munteenheid"
@@ -1719,26 +1802,38 @@ msgstr "Tijdszone"
msgid "Start page"
msgstr "Startpagina"
#: apps/users/views.py:62
#: apps/users/views.py:67
msgid "Transaction amounts are now hidden"
msgstr "Verrichtingsbedragen worden nu verborgen"
#: apps/users/views.py:65
#: apps/users/views.py:70
msgid "Transaction amounts are now displayed"
msgstr "Verrichtingsbedragen worden nu weergegeven"
#: apps/users/views.py:83
#: apps/users/views.py:88
msgid "Sounds are now muted"
msgstr "De Geluiden zijn nu gedempt"
#: apps/users/views.py:86
#: apps/users/views.py:91
msgid "Sounds will now play"
msgstr "De geluiden worden nu afgespeeld"
#: apps/users/views.py:102
#: apps/users/views.py:107
msgid "Your settings have been updated"
msgstr "Jouw instellingen zijn bijgewerkt"
#: apps/users/views.py:151
#, fuzzy
#| msgid "Rule added successfully"
msgid "Item added successfully"
msgstr "Regel succesvol toegevoegd"
#: apps/users/views.py:182
#, fuzzy
#| msgid "Rule updated successfully"
msgid "Item updated successfully"
msgstr "Regel succesvol bijgewerkt"
#: templates/account_groups/fragments/add.html:5
msgid "Add account group"
msgstr "Rekeningsgroep toevoegen"
@@ -1761,6 +1856,7 @@ msgstr "Rekeningsgroep bewerken"
#: templates/recurring_transactions/fragments/table.html:25
#: templates/rules/fragments/list.html:33
#: templates/tags/fragments/table.html:23
#: templates/users/fragments/list.html:38
msgid "Actions"
msgstr "Acties"
@@ -1783,6 +1879,7 @@ msgstr "Acties"
#: templates/rules/fragments/transaction_rule/view.html:47
#: templates/rules/fragments/transaction_rule/view.html:80
#: templates/tags/fragments/table.html:28
#: templates/users/fragments/list.html:43
msgid "Edit"
msgstr "Bewerken"
@@ -2005,7 +2102,7 @@ msgid "Muted"
msgstr "Gedempt"
#: templates/categories/fragments/table.html:75
#: templates/insights/fragments/category_overview/index.html:67
#: templates/insights/fragments/category_overview/index.html:225
msgid "No categories"
msgstr "Geen categorieën"
@@ -2504,15 +2601,19 @@ msgstr "Beheer"
msgid "Automation"
msgstr "Automatisatie"
#: templates/includes/navbar.html:150
#: templates/includes/navbar.html:145
msgid "Admin"
msgstr ""
#: templates/includes/navbar.html:154
msgid "Only use this if you know what you're doing"
msgstr "Gebruik dit alleen als je weet wat je doet"
#: templates/includes/navbar.html:151
#: templates/includes/navbar.html:155
msgid "Django Admin"
msgstr "Django Beheerder"
#: templates/includes/navbar.html:160
#: templates/includes/navbar.html:165
msgid "Calculator"
msgstr "Rekenmachine"
@@ -2520,22 +2621,30 @@ msgstr "Rekenmachine"
msgid "Settings"
msgstr "Instellingen"
#: templates/includes/navbar/user_menu.html:41
#: templates/includes/navbar/user_menu.html:19
#, fuzzy
#| msgid "Edit import profile"
msgid "Edit profile"
msgstr "Importprofiel bewerken"
#: templates/includes/navbar/user_menu.html:46
msgid "Clear cache"
msgstr "Leegmaken"
#: templates/includes/navbar/user_menu.html:45
#: templates/includes/navbar/user_menu.html:50
msgid "Logout"
msgstr "Uitloggen"
#: templates/includes/scripts/hyperscript/htmx_error_handler.html:8
msgid "Access Denied"
msgstr ""
msgstr "Toegang geweigerd"
#: templates/includes/scripts/hyperscript/htmx_error_handler.html:9
msgid ""
"You do not have permission to perform this action or access this resource."
msgstr ""
"Je hebt geen toestemming om deze actie uit te voeren of toegang te krijgen "
"tot deze bron."
#: templates/includes/scripts/hyperscript/htmx_error_handler.html:18
msgid "Something went wrong loading your data"
@@ -2576,11 +2685,15 @@ msgstr "Inkomsten/uitgaven per rekening"
msgid "Income/Expense by Currency"
msgstr "Inkomsten/uitgaven per Munteenheid"
#: templates/insights/fragments/category_overview/index.html:12
#: templates/insights/fragments/category_overview/index.html:13
#: templates/monthly_overview/fragments/monthly_summary.html:167
msgid "Total"
msgstr "Totaal"
#: templates/insights/fragments/category_overview/index.html:202
msgid "Final Total"
msgstr "Eindtotaal"
#: templates/insights/fragments/emergency_fund.html:15
msgid "You've spent an average of"
msgstr "Je bestede gemiddeld"
@@ -2714,11 +2827,12 @@ msgstr "Geen afbetalingsplannen"
#: templates/layouts/base.html:40
msgid "This is a demo!"
msgstr ""
msgstr "Dit is een demo!"
#: templates/layouts/base.html:40
msgid "Any data you add here will be wiped in 24hrs or less"
msgstr ""
"Alle gegevens die je hier toevoegt, worden binnen 24 uur of minder gewist"
#: templates/mini_tools/currency_converter/currency_converter.html:58
msgid "Invert"
@@ -3020,6 +3134,38 @@ msgstr "Verwijderde verrichtingen"
msgid "Unchanged"
msgstr "Ongewijzigd"
#: templates/users/fragments/add.html:5
#, fuzzy
#| msgid "Add new"
msgid "Add user"
msgstr "Nieuwe toevoegen"
#: templates/users/fragments/edit.html:5
#, fuzzy
#| msgid "Edit category"
msgid "Edit user"
msgstr "Categorie bewerken"
#: templates/users/fragments/list.html:30
#, fuzzy
#| msgid "E-mail"
msgid "Email"
msgstr "E-mailadres"
#: templates/users/fragments/list.html:31
msgid "Superuser"
msgstr ""
#: templates/users/fragments/list.html:51
msgid "Impersonate"
msgstr ""
#: templates/users/fragments/list.html:80
#, fuzzy
#| msgid "Users"
msgid "No users"
msgstr "Gebruikers"
#: templates/users/generic/hide_amounts.html:2
msgid "Hide amounts"
msgstr "Bedragen verbergen"
@@ -3038,11 +3184,11 @@ msgstr "Bedragen tonen"
#: templates/users/login.html:17
msgid "Welcome to WYGIWYH's demo!"
msgstr ""
msgstr "Welkom bij de demo van WYGIWYH!"
#: templates/users/login.html:18
msgid "Use the credentials below to login"
msgstr ""
msgstr "Gebruik de onderstaande gegevens om in te loggen"
#: templates/yearly_overview/pages/overview_by_account.html:7
#: templates/yearly_overview/pages/overview_by_currency.html:9

File diff suppressed because it is too large Load Diff

View File

@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-03-31 05:30+0000\n"
"PO-Revision-Date: 2025-03-31 05:38+0000\n"
"POT-Creation-Date: 2025-04-13 22:01+0000\n"
"PO-Revision-Date: 2025-04-13 08:16+0000\n"
"Last-Translator: Herculino Trotta <netotrotta@gmail.com>\n"
"Language-Team: Portuguese (Brazil) <https://translations.herculino.com/"
"projects/wygiwyh/app/pt_BR/>\n"
@@ -31,6 +31,7 @@ msgstr "Nome do grupo"
#: apps/transactions/forms.py:269 apps/transactions/forms.py:629
#: apps/transactions/forms.py:672 apps/transactions/forms.py:704
#: apps/transactions/forms.py:739 apps/transactions/forms.py:891
#: apps/users/forms.py:210 apps/users/forms.py:372
msgid "Update"
msgstr "Atualizar"
@@ -42,8 +43,8 @@ msgstr "Atualizar"
#: apps/transactions/forms.py:187 apps/transactions/forms.py:211
#: apps/transactions/forms.py:637 apps/transactions/forms.py:680
#: apps/transactions/forms.py:712 apps/transactions/forms.py:747
#: apps/transactions/forms.py:899
#: templates/account_groups/fragments/list.html:9
#: apps/transactions/forms.py:899 apps/users/forms.py:218
#: apps/users/forms.py:380 templates/account_groups/fragments/list.html:9
#: templates/accounts/fragments/list.html:9
#: templates/categories/fragments/list.html:9
#: templates/currencies/fragments/list.html:9
@@ -58,6 +59,7 @@ msgstr "Atualizar"
#: templates/mini_tools/unit_price_calculator.html:162
#: templates/recurring_transactions/fragments/list.html:9
#: templates/rules/fragments/list.html:9 templates/tags/fragments/list.html:9
#: templates/users/fragments/list.html:10
msgid "Add"
msgstr "Adicionar"
@@ -75,8 +77,9 @@ msgstr "Novo saldo"
#: apps/transactions/forms.py:40 apps/transactions/forms.py:303
#: apps/transactions/forms.py:310 apps/transactions/forms.py:510
#: apps/transactions/forms.py:771 apps/transactions/models.py:305
#: apps/transactions/models.py:488 apps/transactions/models.py:686
#: templates/insights/fragments/category_overview/index.html:9
#: apps/transactions/models.py:488 apps/transactions/models.py:688
#: templates/insights/fragments/category_overview/index.html:10
#: templates/insights/fragments/category_overview/index.html:215
msgid "Category"
msgstr "Categoria"
@@ -87,7 +90,7 @@ msgstr "Categoria"
#: apps/transactions/forms.py:48 apps/transactions/forms.py:319
#: apps/transactions/forms.py:327 apps/transactions/forms.py:503
#: apps/transactions/forms.py:764 apps/transactions/models.py:311
#: apps/transactions/models.py:490 apps/transactions/models.py:690
#: apps/transactions/models.py:490 apps/transactions/models.py:692
#: templates/includes/navbar.html:108 templates/tags/fragments/list.html:5
#: templates/tags/pages/index.html:4
msgid "Tags"
@@ -108,6 +111,7 @@ msgstr "Tags"
#: templates/recurring_transactions/fragments/table.html:18
#: templates/rules/fragments/list.html:26
#: templates/tags/fragments/table.html:16
#: templates/users/fragments/list.html:29
msgid "Name"
msgstr "Nome"
@@ -162,7 +166,7 @@ msgstr ""
#: apps/rules/models.py:30 apps/rules/models.py:242
#: apps/transactions/forms.py:60 apps/transactions/forms.py:495
#: apps/transactions/forms.py:756 apps/transactions/models.py:278
#: apps/transactions/models.py:448 apps/transactions/models.py:668
#: apps/transactions/models.py:448 apps/transactions/models.py:670
msgid "Account"
msgstr "Conta"
@@ -284,7 +288,11 @@ msgstr "Erro criando nova instância"
msgid "Ungrouped"
msgstr "Não agrupado"
#: apps/common/fields/month_year.py:23 apps/common/fields/month_year.py:51
#: apps/common/fields/month_year.py:30
msgid "Invalid date format. Use YYYY-MM or YYYY-MM-DD."
msgstr "Formato de data inválido. Use AAAA-MM ou AAAA-MM-DD."
#: apps/common/fields/month_year.py:59
msgid "Invalid date format. Use YYYY-MM."
msgstr "Formato de data inválido. Use AAAA-MM."
@@ -321,7 +329,7 @@ msgstr ""
"Somente editável pelo proprietário.<br/>Público: Exibido para todos os "
"usuários. Somente editável pelo proprietário."
#: apps/common/forms.py:79 apps/users/forms.py:131
#: apps/common/forms.py:79 apps/users/forms.py:135
msgid "Save"
msgstr "Salvar"
@@ -412,7 +420,7 @@ msgstr "Erro"
msgid "Info"
msgstr "Informação"
#: apps/common/views.py:110
#: apps/common/views.py:111
msgid "Cache cleared successfully"
msgstr "Cache limpo com sucesso"
@@ -537,7 +545,7 @@ msgstr "Tipo de Serviço"
#: templates/categories/fragments/list.html:21
#: templates/entities/fragments/list.html:21
#: templates/recurring_transactions/fragments/list.html:21
#: templates/tags/fragments/list.html:21
#: templates/tags/fragments/list.html:21 templates/users/fragments/list.html:28
msgid "Active"
msgstr "Ativo"
@@ -712,7 +720,7 @@ msgstr "Moeda de pagamento"
#: apps/dca/models.py:26 apps/dca/models.py:181 apps/rules/forms.py:173
#: apps/rules/forms.py:188 apps/rules/models.py:37 apps/rules/models.py:270
#: apps/transactions/forms.py:347 apps/transactions/models.py:301
#: apps/transactions/models.py:497 apps/transactions/models.py:696
#: apps/transactions/models.py:497 apps/transactions/models.py:698
msgid "Notes"
msgstr "Notas"
@@ -769,6 +777,8 @@ msgid "Entry deleted successfully"
msgstr "Entrada apagada com sucesso"
#: apps/export_app/forms.py:14 apps/export_app/forms.py:131
#: templates/includes/navbar.html:147 templates/users/fragments/list.html:6
#: templates/users/pages/index.html:4
msgid "Users"
msgstr "Usuários"
@@ -793,13 +803,13 @@ msgstr "Categorias"
#: apps/transactions/forms.py:56 apps/transactions/forms.py:518
#: apps/transactions/forms.py:779 apps/transactions/models.py:261
#: apps/transactions/models.py:316 apps/transactions/models.py:493
#: apps/transactions/models.py:693 templates/entities/fragments/list.html:5
#: apps/transactions/models.py:695 templates/entities/fragments/list.html:5
#: templates/entities/pages/index.html:4 templates/includes/navbar.html:110
msgid "Entities"
msgstr "Entidades"
#: apps/export_app/forms.py:56 apps/export_app/forms.py:140
#: apps/transactions/models.py:730 templates/includes/navbar.html:74
#: apps/transactions/models.py:732 templates/includes/navbar.html:74
#: templates/recurring_transactions/fragments/list.html:5
#: templates/recurring_transactions/pages/index.html:4
msgid "Recurring Transactions"
@@ -949,7 +959,7 @@ msgstr "Importação atualizada com sucesso"
#: apps/import_app/views.py:130
msgid "Import Profile deleted successfully"
msgstr "Importação apagada com sucesso"
msgstr "Perfil de Importação apagado com sucesso"
#: apps/import_app/views.py:195
msgid "Import Run queued successfully"
@@ -961,7 +971,9 @@ msgstr "Importação apagada com sucesso"
#: apps/insights/forms.py:119 apps/insights/utils/sankey.py:36
#: apps/insights/utils/sankey.py:167
#: templates/insights/fragments/category_overview/index.html:18
#: templates/insights/fragments/category_overview/index.html:19
#: templates/insights/fragments/category_overview/index.html:87
#: templates/insights/fragments/category_overview/index.html:116
msgid "Uncategorized"
msgstr "Sem categoria"
@@ -1044,7 +1056,7 @@ msgstr "Operador"
#: apps/rules/forms.py:167 apps/rules/forms.py:180 apps/rules/models.py:31
#: apps/rules/models.py:246 apps/transactions/models.py:285
#: apps/transactions/models.py:453 apps/transactions/models.py:674
#: apps/transactions/models.py:453 apps/transactions/models.py:676
msgid "Type"
msgstr "Tipo"
@@ -1061,20 +1073,20 @@ msgstr "Pago"
#: apps/rules/models.py:258 apps/transactions/forms.py:68
#: apps/transactions/forms.py:334 apps/transactions/forms.py:524
#: apps/transactions/models.py:289 apps/transactions/models.py:471
#: apps/transactions/models.py:698
#: apps/transactions/models.py:700
msgid "Reference Date"
msgstr "Data de Referência"
#: apps/rules/forms.py:171 apps/rules/forms.py:184 apps/rules/models.py:35
#: apps/rules/models.py:262 apps/transactions/models.py:294
#: apps/transactions/models.py:679 templates/insights/fragments/sankey.html:95
#: apps/transactions/models.py:681 templates/insights/fragments/sankey.html:95
msgid "Amount"
msgstr "Quantia"
#: apps/rules/forms.py:172 apps/rules/forms.py:185 apps/rules/models.py:14
#: apps/rules/models.py:36 apps/rules/models.py:266
#: apps/transactions/forms.py:338 apps/transactions/models.py:299
#: apps/transactions/models.py:455 apps/transactions/models.py:682
#: apps/transactions/models.py:455 apps/transactions/models.py:684
msgid "Description"
msgstr "Descrição"
@@ -1332,7 +1344,7 @@ msgstr "Entidade"
#: templates/calendar_view/fragments/list.html:52
#: templates/calendar_view/fragments/list.html:54
#: templates/cotton/ui/quick_transactions_buttons.html:10
#: templates/insights/fragments/category_overview/index.html:10
#: templates/insights/fragments/category_overview/index.html:11
#: templates/monthly_overview/fragments/monthly_summary.html:39
msgid "Income"
msgstr "Renda"
@@ -1343,7 +1355,7 @@ msgstr "Renda"
#: templates/calendar_view/fragments/list.html:56
#: templates/calendar_view/fragments/list.html:58
#: templates/cotton/ui/quick_transactions_buttons.html:18
#: templates/insights/fragments/category_overview/index.html:11
#: templates/insights/fragments/category_overview/index.html:12
msgid "Expense"
msgstr "Despesa"
@@ -1351,7 +1363,7 @@ msgstr "Despesa"
msgid "Installment Plan"
msgstr "Parcelamento"
#: apps/transactions/models.py:336 apps/transactions/models.py:729
#: apps/transactions/models.py:336 apps/transactions/models.py:731
msgid "Recurring Transaction"
msgstr "Transação Recorrente"
@@ -1408,11 +1420,11 @@ msgstr "Parcela inicial"
msgid "The installment number to start counting from"
msgstr "O número da parcela a partir do qual se inicia a contagem"
#: apps/transactions/models.py:469 apps/transactions/models.py:702
#: apps/transactions/models.py:469 apps/transactions/models.py:704
msgid "Start Date"
msgstr "Data de Início"
#: apps/transactions/models.py:473 apps/transactions/models.py:703
#: apps/transactions/models.py:473 apps/transactions/models.py:705
msgid "End Date"
msgstr "Data Final"
@@ -1424,48 +1436,48 @@ msgstr "Recorrência"
msgid "Installment Amount"
msgstr "Valor da Parcela"
#: apps/transactions/models.py:500 apps/transactions/models.py:719
#: apps/transactions/models.py:500 apps/transactions/models.py:721
msgid "Add description to transactions"
msgstr "Adicionar descrição às transações"
#: apps/transactions/models.py:503 apps/transactions/models.py:722
#: apps/transactions/models.py:503 apps/transactions/models.py:724
msgid "Add notes to transactions"
msgstr "Adicionar notas às transações"
#: apps/transactions/models.py:661
#: apps/transactions/models.py:663
msgid "day(s)"
msgstr "dia(s)"
#: apps/transactions/models.py:662
#: apps/transactions/models.py:664
msgid "week(s)"
msgstr "semana(s)"
#: apps/transactions/models.py:663
#: apps/transactions/models.py:665
msgid "month(s)"
msgstr "mês(es)"
#: apps/transactions/models.py:664
#: apps/transactions/models.py:666
msgid "year(s)"
msgstr "ano(s)"
#: apps/transactions/models.py:666
#: apps/transactions/models.py:668
#: templates/recurring_transactions/fragments/list.html:24
msgid "Paused"
msgstr "Pausado"
#: apps/transactions/models.py:705
#: apps/transactions/models.py:707
msgid "Recurrence Type"
msgstr "Tipo de recorrência"
#: apps/transactions/models.py:708
#: apps/transactions/models.py:710
msgid "Recurrence Interval"
msgstr "Intervalo de recorrência"
#: apps/transactions/models.py:712
#: apps/transactions/models.py:714
msgid "Last Generated Date"
msgstr "Última data gerada"
#: apps/transactions/models.py:715
#: apps/transactions/models.py:717
msgid "Last Generated Reference Date"
msgstr "Última data de referência gerada"
@@ -1642,40 +1654,111 @@ msgstr "Permissões"
msgid "Important dates"
msgstr "Datas importantes"
#: apps/users/forms.py:19 apps/users/models.py:13 templates/users/login.html:19
#: apps/users/forms.py:23 apps/users/models.py:13 templates/users/login.html:19
msgid "E-mail"
msgstr "E-mail"
#: apps/users/forms.py:25 templates/users/login.html:20
#: apps/users/forms.py:29 templates/users/login.html:20
msgid "Password"
msgstr "Senha"
#: apps/users/forms.py:33
#: apps/users/forms.py:37
msgid "Invalid e-mail or password"
msgstr "E-mail ou senha inválidos"
#: apps/users/forms.py:34
#: apps/users/forms.py:38
msgid "This account is deactivated"
msgstr "Essa conta está desativada"
#: apps/users/forms.py:50 apps/users/forms.py:63 apps/users/forms.py:85
#: apps/users/forms.py:54 apps/users/forms.py:67 apps/users/forms.py:89
#: templates/monthly_overview/pages/overview.html:153
#: templates/transactions/pages/transactions.html:35
msgid "Default"
msgstr "Padrão"
#: apps/users/forms.py:91 apps/users/models.py:41
#: apps/users/forms.py:95 apps/users/models.py:41
msgid "Date Format"
msgstr "Formato de Data"
#: apps/users/forms.py:96 apps/users/models.py:46
#: apps/users/forms.py:100 apps/users/models.py:46
msgid "Datetime Format"
msgstr "Formato de Data e Hora"
#: apps/users/forms.py:102 apps/users/models.py:49
#: apps/users/forms.py:106 apps/users/models.py:49
msgid "Number Format"
msgstr "Formato de Número"
#: apps/users/forms.py:141
#, python-format
msgid ""
"This changes the language (if available) and how numbers and dates are "
"displayed\n"
"Consider helping translate WYGIWYH to your language at %(translation_link)s"
msgstr ""
"Isso altera o idioma (se disponível) e a forma como os números e as datas "
"são exibidos\n"
"Considere ajudar a traduzir WYGIWYH para seu idioma em %(translation_link)s"
#: apps/users/forms.py:150
#, fuzzy
#| msgid "Password"
msgid "New Password"
msgstr "Senha"
#: apps/users/forms.py:153
msgid "Leave blank to keep the current password."
msgstr ""
#: apps/users/forms.py:156
msgid "Confirm New Password"
msgstr ""
#: apps/users/forms.py:168 apps/users/forms.py:329
msgid ""
"Designates whether this user should be treated as active. Unselect this "
"instead of deleting accounts."
msgstr ""
#: apps/users/forms.py:171 apps/users/forms.py:332
msgid ""
"Designates that this user has all permissions without explicitly assigning "
"them."
msgstr ""
#: apps/users/forms.py:242
msgid "This email address is already in use by another account."
msgstr ""
#: apps/users/forms.py:250
msgid "The two password fields didn't match."
msgstr ""
#: apps/users/forms.py:252
msgid "Please confirm your new password."
msgstr ""
#: apps/users/forms.py:254
msgid "Please enter the new password first."
msgstr ""
#: apps/users/forms.py:274
msgid "You cannot deactivate your own account using this form."
msgstr ""
#: apps/users/forms.py:287
msgid "Cannot remove status from the last superuser."
msgstr ""
#: apps/users/forms.py:293
msgid "You cannot remove your own superuser status using this form."
msgstr ""
#: apps/users/forms.py:390
#, fuzzy
#| msgid "A value for this field already exists in the rule."
msgid "A user with this email address already exists."
msgstr "Já existe um valor para esse campo na regra."
#: apps/users/models.py:27 templates/includes/navbar.html:28
msgid "Yearly by currency"
msgstr "Anual por moeda"
@@ -1716,26 +1799,38 @@ msgstr "Fuso horário"
msgid "Start page"
msgstr "Página inicial"
#: apps/users/views.py:62
#: apps/users/views.py:67
msgid "Transaction amounts are now hidden"
msgstr "Os valores das transações agora estão ocultos"
#: apps/users/views.py:65
#: apps/users/views.py:70
msgid "Transaction amounts are now displayed"
msgstr "Os valores das transações agora estão sendo exibidos"
#: apps/users/views.py:83
#: apps/users/views.py:88
msgid "Sounds are now muted"
msgstr "Os sons agora estão silenciados"
#: apps/users/views.py:86
#: apps/users/views.py:91
msgid "Sounds will now play"
msgstr "Os sons agora serão reproduzidos"
#: apps/users/views.py:102
#: apps/users/views.py:107
msgid "Your settings have been updated"
msgstr "Suas configurações foram atualizadas"
#: apps/users/views.py:151
#, fuzzy
#| msgid "Rule added successfully"
msgid "Item added successfully"
msgstr "Regra adicionada com sucesso"
#: apps/users/views.py:182
#, fuzzy
#| msgid "Rule updated successfully"
msgid "Item updated successfully"
msgstr "Regra atualizada com sucesso"
#: templates/account_groups/fragments/add.html:5
msgid "Add account group"
msgstr "Adicionar grupo de conta"
@@ -1758,6 +1853,7 @@ msgstr "Editar grupo de conta"
#: templates/recurring_transactions/fragments/table.html:25
#: templates/rules/fragments/list.html:33
#: templates/tags/fragments/table.html:23
#: templates/users/fragments/list.html:38
msgid "Actions"
msgstr "Ações"
@@ -1780,6 +1876,7 @@ msgstr "Ações"
#: templates/rules/fragments/transaction_rule/view.html:47
#: templates/rules/fragments/transaction_rule/view.html:80
#: templates/tags/fragments/table.html:28
#: templates/users/fragments/list.html:43
msgid "Edit"
msgstr "Editar"
@@ -2002,7 +2099,7 @@ msgid "Muted"
msgstr "Silenciada"
#: templates/categories/fragments/table.html:75
#: templates/insights/fragments/category_overview/index.html:67
#: templates/insights/fragments/category_overview/index.html:225
msgid "No categories"
msgstr "Nenhum categoria"
@@ -2503,15 +2600,19 @@ msgstr "Gerenciar"
msgid "Automation"
msgstr "Automação"
#: templates/includes/navbar.html:150
#: templates/includes/navbar.html:145
msgid "Admin"
msgstr ""
#: templates/includes/navbar.html:154
msgid "Only use this if you know what you're doing"
msgstr "Só use isso se você souber o que está fazendo"
#: templates/includes/navbar.html:151
#: templates/includes/navbar.html:155
msgid "Django Admin"
msgstr "Django Admin"
#: templates/includes/navbar.html:160
#: templates/includes/navbar.html:165
msgid "Calculator"
msgstr "Calculadora"
@@ -2519,11 +2620,17 @@ msgstr "Calculadora"
msgid "Settings"
msgstr "Configurações"
#: templates/includes/navbar/user_menu.html:41
#: templates/includes/navbar/user_menu.html:19
#, fuzzy
#| msgid "Edit import profile"
msgid "Edit profile"
msgstr "Editar perfil de importação"
#: templates/includes/navbar/user_menu.html:46
msgid "Clear cache"
msgstr "Limpar cache"
#: templates/includes/navbar/user_menu.html:45
#: templates/includes/navbar/user_menu.html:50
msgid "Logout"
msgstr "Sair"
@@ -2534,7 +2641,8 @@ msgstr "Acesso Negado"
#: templates/includes/scripts/hyperscript/htmx_error_handler.html:9
msgid ""
"You do not have permission to perform this action or access this resource."
msgstr "Você não tem permissão para executar essa ação ou acessar esse recurso."
msgstr ""
"Você não tem permissão para executar essa ação ou acessar esse recurso."
#: templates/includes/scripts/hyperscript/htmx_error_handler.html:18
msgid "Something went wrong loading your data"
@@ -2574,11 +2682,15 @@ msgstr "Gasto/Despesa por Conta"
msgid "Income/Expense by Currency"
msgstr "Gasto/Despesa por Moeda"
#: templates/insights/fragments/category_overview/index.html:12
#: templates/insights/fragments/category_overview/index.html:13
#: templates/monthly_overview/fragments/monthly_summary.html:167
msgid "Total"
msgstr "Total"
#: templates/insights/fragments/category_overview/index.html:202
msgid "Final Total"
msgstr "Total Final"
#: templates/insights/fragments/emergency_fund.html:15
msgid "You've spent an average of"
msgstr "Você gastou em média"
@@ -3016,6 +3128,38 @@ msgstr "Transações apagadas"
msgid "Unchanged"
msgstr "Inalterado"
#: templates/users/fragments/add.html:5
#, fuzzy
#| msgid "Add new"
msgid "Add user"
msgstr "Adicionar novo"
#: templates/users/fragments/edit.html:5
#, fuzzy
#| msgid "Edit category"
msgid "Edit user"
msgstr "Editar categoria"
#: templates/users/fragments/list.html:30
#, fuzzy
#| msgid "E-mail"
msgid "Email"
msgstr "E-mail"
#: templates/users/fragments/list.html:31
msgid "Superuser"
msgstr ""
#: templates/users/fragments/list.html:51
msgid "Impersonate"
msgstr ""
#: templates/users/fragments/list.html:80
#, fuzzy
#| msgid "Users"
msgid "No users"
msgstr "Usuários"
#: templates/users/generic/hide_amounts.html:2
msgid "Hide amounts"
msgstr "Esconder valores"

View File

@@ -1,27 +1,27 @@
{% load i18n %}
<div class="progress-stacked">
<div class="progress position-relative" role="progressbar" aria-label="{% trans 'Projected Income' %} ({{ percentage.percentages.income_projected|floatformat:2 }}%)" aria-valuenow="{{ percentage.percentages.expense_projected|floatformat:0 }}" aria-valuemin="0" aria-valuemax="100" style="width: {{ percentage.percentages.income_projected|floatformat:0 }}%">
<div class="progress position-relative" role="progressbar" aria-label="{% trans 'Projected Income' %} ({{ percentage.percentages.income_projected|floatformat:2 }}%)" aria-valuenow="{{ percentage.percentages.expense_projected|floatformat:0 }}" aria-valuemin="0" aria-valuemax="100" style="width: {{ percentage.percentages.income_projected|floatformat:"2u" }}%">
<div class="progress-bar progress-bar-striped !tw-bg-green-300"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="{% trans 'Projected Income' %} ({{ percentage.percentages.income_projected|floatformat:2 }}%)">
</div>
</div>
<div class="progress position-relative" role="progressbar" aria-label="{% trans 'Current Income' %} ({{ percentage.percentages.income_current|floatformat:2 }}%)" aria-valuenow="{{ percentage.percentages.expense_projected|floatformat:0 }}" aria-valuemin="0" aria-valuemax="100" style="width: {{ percentage.percentages.income_current|floatformat:0 }}%">
<div class="progress position-relative" role="progressbar" aria-label="{% trans 'Current Income' %} ({{ percentage.percentages.income_current|floatformat:2 }}%)" aria-valuenow="{{ percentage.percentages.expense_projected|floatformat:0 }}" aria-valuemin="0" aria-valuemax="100" style="width: {{ percentage.percentages.income_current|floatformat:"2u" }}%">
<div class="progress-bar !tw-bg-green-400"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="{% trans 'Current Income' %} ({{ p.percentages.income_current|floatformat:2 }}%)">
</div>
</div>
<div class="progress position-relative" role="progressbar" aria-label="{% trans 'Projected Expenses' %} ({{ percentage.percentages.expense_projected|floatformat:2 }}%)" aria-valuenow="{{ percentage.percentages.expense_projected|floatformat:0 }}" aria-valuemin="0" aria-valuemax="100" style="width: {{ percentage.percentages.expense_projected|floatformat:0 }}%">
<div class="progress position-relative" role="progressbar" aria-label="{% trans 'Projected Expenses' %} ({{ percentage.percentages.expense_projected|floatformat:2 }}%)" aria-valuenow="{{ percentage.percentages.expense_projected|floatformat:0 }}" aria-valuemin="0" aria-valuemax="100" style="width: {{ percentage.percentages.expense_projected|floatformat:"2u" }}%">
<div class="progress-bar progress-bar-striped !tw-bg-red-300"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="{% trans 'Projected Expenses' %} ({{ percentage.percentages.expense_projected|floatformat:2 }}%)">
</div>
</div>
<div class="progress position-relative" role="progressbar" aria-label="{% trans 'Current Expenses' %} ({{ percentage.percentages.expense_current|floatformat:2 }}%)" aria-valuenow="{{ percentage.percentages.expense_projected|floatformat:0 }}" aria-valuemin="0" aria-valuemax="100" style="width: {{ percentage.percentages.expense_current|floatformat:0 }}%">
<div class="progress position-relative" role="progressbar" aria-label="{% trans 'Current Expenses' %} ({{ percentage.percentages.expense_current|floatformat:2 }}%)" aria-valuenow="{{ percentage.percentages.expense_projected|floatformat:0 }}" aria-valuemin="0" aria-valuemax="100" style="width: {{ percentage.percentages.expense_current|floatformat:"2u" }}%">
<div class="progress-bar !tw-bg-red-400"
data-bs-toggle="tooltip"
data-bs-placement="top"

View File

@@ -138,9 +138,13 @@
{% endif %}
<li><a class="dropdown-item {% active_link views='automatic_exchange_rates_index' %}"
href="{% url 'automatic_exchange_rates_index' %}">{% translate 'Automatic Exchange Rates' %}</a></li>
{% if user.is_superuser %}
<li>
<hr class="dropdown-divider">
</li>
<li><h6 class="dropdown-header">{% trans 'Admin' %}</h6></li>
<li><a class="dropdown-item {% active_link views='users_index' %}"
href="{% url 'users_index' %}">{% translate 'Users' %}</a></li>
<li>
<a class="dropdown-item"
href="{% url 'admin:index' %}"
@@ -151,6 +155,7 @@
{% translate 'Django Admin' %}
</a>
</li>
{% endif %}
</ul>
</li>
</ul>

View File

@@ -12,6 +12,11 @@
hx-target="#generic-offcanvas"
role="button">
<i class="fa-solid fa-gear me-2 fa-fw"></i>{% translate 'Settings' %}</a></li>
<li><a class="dropdown-item"
hx-get="{% url 'user_edit' pk=request.user.id %}"
hx-target="#generic-offcanvas"
role="button">
<i class="fa-solid fa-user me-2 fa-fw"></i>{% translate 'Edit profile' %}</a></li>
<li><hr class="dropdown-divider"></li>
{% spaceless %}
<li>

View File

@@ -1,68 +1,226 @@
{% load i18n %}
<div hx-get="{% url 'category_overview' %}" hx-trigger="updated from:window" class="show-loading" hx-swap="outerHTML" hx-include="#picker-form, #picker-type">
<div hx-get="{% url 'category_overview' %}" hx-trigger="updated from:window" class="show-loading" hx-swap="outerHTML"
hx-include="#picker-form, #picker-type">
{% if total_table %}
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead>
<tr>
<th scope="col">{% trans 'Category' %}</th>
<th scope="col">{% trans 'Income' %}</th>
<th scope="col">{% trans 'Expense' %}</th>
<th scope="col">{% trans 'Total' %}</th>
</tr>
</thead>
<tbody>
{% for category in total_table.values %}
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>{% if category.name %}{{ category.name }}{% else %}{% trans 'Uncategorized' %}{% endif %}</th>
<td>
{% for currency in category.currencies.values %}
{% if currency.total_income != 0 %}
<c-amount.display
:amount="currency.total_income"
:prefix="currency.currency.prefix"
:suffix="currency.currency.suffix"
:decimal_places="currency.currency.decimal_places"
color="green"></c-amount.display>
{% else %}
<div>-</div>
{% endif %}
{% endfor %}
</td>
<td>
{% for currency in category.currencies.values %}
{% if currency.total_expense != 0 %}
<c-amount.display
:amount="currency.total_expense"
:prefix="currency.currency.prefix"
:suffix="currency.currency.suffix"
:decimal_places="currency.currency.decimal_places"
color="red"></c-amount.display>
{% else %}
<div>-</div>
{% endif %}
{% endfor %}
</td>
<td>
{% for currency in category.currencies.values %}
{% if currency.total_final != 0 %}
<c-amount.display
:amount="currency.total_final"
:prefix="currency.currency.prefix"
:suffix="currency.currency.suffix"
:decimal_places="currency.currency.decimal_places"
color="{% if currency.total_final < 0 %}red{% else %}green{% endif %}"></c-amount.display>
{% else %}
<div>-</div>
{% endif %}
{% endfor %}
</td>
<th scope="col">{% trans 'Category' %}</th>
<th scope="col">{% trans 'Income' %}</th>
<th scope="col">{% trans 'Expense' %}</th>
<th scope="col">{% trans 'Total' %}</th>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</thead>
<tbody>
{% for category in total_table.values %}
<tr>
<th>{% if category.name %}{{ category.name }}{% else %}{% trans 'Uncategorized' %}{% endif %}</th>
<td>
{% for currency in category.currencies.values %}
{% if currency.total_income != 0 %}
<c-amount.display
:amount="currency.total_income"
:prefix="currency.currency.prefix"
:suffix="currency.currency.suffix"
:decimal_places="currency.currency.decimal_places"
color="green"></c-amount.display>
{% else %}
<div>-</div>
{% endif %}
{% endfor %}
</td>
<td>
{% for currency in category.currencies.values %}
{% if currency.total_expense != 0 %}
<c-amount.display
:amount="currency.total_expense"
:prefix="currency.currency.prefix"
:suffix="currency.currency.suffix"
:decimal_places="currency.currency.decimal_places"
color="red"></c-amount.display>
{% else %}
<div>-</div>
{% endif %}
{% endfor %}
</td>
<td>
{% for currency in category.currencies.values %}
{% if currency.total_final != 0 %}
<c-amount.display
:amount="currency.total_final"
:prefix="currency.currency.prefix"
:suffix="currency.currency.suffix"
:decimal_places="currency.currency.decimal_places"
color="{% if currency.total_final < 0 %}red{% else %}green{% endif %}"></c-amount.display>
{% else %}
<div>-</div>
{% endif %}
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="mt-4">
<div class="chart-container" _="init call setupChart() end" style="position: relative; height:90vh; width:100%">
<canvas id="categoryChart"></canvas>
</div>
</div>
{{ total_table|json_script:"categoryOverviewData" }}
<script>
function setupChart() {
var rawData = JSON.parse(document.getElementById('categoryOverviewData').textContent);
// --- Dynamic Data Processing ---
var categories = [];
var currencyDetails = {}; // Stores details like { BRL: {code: 'BRL', name: 'Real', ...}, ... }
var currencyData = {}; // Stores data arrays like { BRL: [val1, null, val3,...], ... }
// Pass 1: Collect categories and currency details
Object.values(rawData).forEach(cat => {
var categoryName = cat.name === null ? "{% trans 'Uncategorized' %}" : cat.name;
if (!categories.includes(categoryName)) {
categories.push(categoryName);
}
if (cat.currencies) {
Object.values(cat.currencies).forEach(curr => {
var details = curr.currency;
if (details && details.code && !currencyDetails[details.code]) {
var decimals = parseInt(details.decimal_places, 10);
currencyDetails[details.code] = {
code: details.code,
name: details.name || details.code,
prefix: details.prefix || '',
suffix: details.suffix || '',
// Ensure decimal_places is a non-negative integer
decimal_places: !isNaN(decimals) && decimals >= 0 ? decimals : 2
};
}
});
}
});
// Initialize data structure for each currency with nulls
Object.keys(currencyDetails).forEach(code => {
currencyData[code] = new Array(categories.length).fill(null);
});
// Pass 2: Populate data arrays (store all valid numbers now)
Object.values(rawData).forEach(cat => {
var categoryName = cat.name === null ? "{% trans 'Uncategorized' %}" : cat.name;
var catIndex = categories.indexOf(categoryName);
if (catIndex === -1) return;
if (cat.currencies) {
Object.values(cat.currencies).forEach(curr => {
var code = curr.currency?.code;
if (code && currencyData[code]) {
var value = parseFloat(curr.total_final);
// Store the number if it's valid, otherwise keep null
currencyData[code][catIndex] = !isNaN(value) ? value : null;
}
});
}
});
// --- Dynamic Chart Configuration ---
var datasets = Object.keys(currencyDetails).map((code, index) => {
return {
label: currencyDetails[code].name, // Use currency name for the legend label
data: currencyData[code],
currencyCode: code, // Store code for easy lookup in tooltip
borderWidth: 1
};
});
new Chart(document.getElementById('categoryChart'),
{
type: 'bar',
data: {
labels: categories,
datasets: datasets
},
options: {
indexAxis: 'y',
responsive: true,
interaction: {
intersect: false,
mode: 'nearest',
axis: "y"
},
maintainAspectRatio: false,
plugins: {
title: {
display: false
},
tooltip: {
callbacks: {
label: function (context) {
const dataset = context.dataset;
const currencyCode = dataset.currencyCode;
const details = currencyDetails[currencyCode];
const value = context.parsed.x; // Use 'x' because indexAxis is 'y'
if (value === null || value === undefined || !details) {
// Display the category name if the value is null/undefined
return null;
}
let formattedValue = '';
try {
// Use Intl.NumberFormat for ALL values, configured with locale and exact decimal places
formattedValue = new Intl.NumberFormat(undefined, {
minimumFractionDigits: details.decimal_places,
maximumFractionDigits: details.decimal_places,
// Do NOT use style: 'currency' here, as we add prefix/suffix manually
}).format(value);
} catch (e) {
formattedValue = value.toFixed(details.decimal_places);
}
// Return label with currency name and formatted value including prefix/suffix
return `${details.prefix}${formattedValue}${details.suffix}`;
}
}
},
legend: {
position: 'top',
}
},
scales: {
x: {
stacked: true,
type: 'linear',
title: {
display: true,
text: '{% trans 'Final Total' %}'
},
ticks: {
// Format ticks using the detected locale
callback: function (value, index, ticks) {
return value.toLocaleString();
}
}
},
y: {
stacked: true,
title: {
display: false,
text: '{% trans 'Category' %}'
}
}
}
}
});
}
</script>
{% else %}
<c-msg.empty title="{% translate "No categories" %}"></c-msg.empty>
{% endif %}

View File

@@ -0,0 +1,11 @@
{% extends 'extends/offcanvas.html' %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block title %}{% translate 'Add user' %}{% endblock %}
{% block body %}
<form hx-post="{% url 'user_add' %}" hx-target="#generic-offcanvas" novalidate>
{% crispy form %}
</form>
{% endblock %}

View File

@@ -0,0 +1,11 @@
{% extends 'extends/offcanvas.html' %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block title %}{% translate 'Edit user' %}{% endblock %}
{% block body %}
<form hx-post="{% url 'user_edit' pk=user.id %}" hx-target="#generic-offcanvas" novalidate>
{% crispy form %}
</form>
{% endblock %}

View File

@@ -0,0 +1,85 @@
{% load hijack %}
{% load i18n %}
<div class="container px-md-3 py-3 column-gap-5">
<div class="tw-text-3xl fw-bold font-monospace tw-w-full mb-3">
{% spaceless %}
<div>{% translate 'Users' %}<span>
<a class="text-decoration-none tw-text-2xl p-1 category-action"
role="button"
data-bs-toggle="tooltip"
data-bs-title="{% translate "Add" %}"
hx-get="{% url 'user_add' %}"
hx-target="#generic-offcanvas">
<i class="fa-solid fa-circle-plus fa-fw"></i></a>
</span></div>
{% endspaceless %}
</div>
<div class="card">
<div class="card-body">
<div id="tags-table">
{% if users %}
<div class="table-responsive">
<c-config.search></c-config.search>
<table class="table table-hover">
<thead>
<tr>
<th scope="col" class="col-auto"></th>
<th scope="col" class="col">{% translate 'Active' %}</th>
<th scope="col" class="col">{% translate 'Name' %}</th>
<th scope="col" class="col">{% translate 'Email' %}</th>
<th scope="col" class="col">{% translate 'Superuser' %}</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr class="tag">
<td class="col-auto">
<div class="btn-group" role="group" aria-label="{% translate 'Actions' %}">
<a class="btn btn-secondary btn-sm"
role="button"
hx-swap="innerHTML"
data-bs-toggle="tooltip"
data-bs-title="{% translate "Edit" %}"
hx-get="{% url 'user_edit' pk=user.id %}"
hx-target="#generic-offcanvas">
<i class="fa-solid fa-pencil fa-fw"></i></a>
{% if request.user|can_hijack:user and request.user != user %}
<a class="btn btn-info btn-sm"
role="button"
data-bs-toggle="tooltip"
data-bs-title="{% translate "Impersonate" %}"
hx-post="{% url 'hijack:acquire' %}"
hx-vals='{"user_pk":"{{user.id}}"}'
hx-swap="none"
_="on htmx:afterRequest(event) from me
if event.detail.successful
go to url '/'">
<i class="fa-solid fa-mask fa-fw"></i></a>
{% endif %}
</div>
</td>
<td class="col">
{% if user.is_active %}
<i class="fa-solid fa-solid fa-check text-success"></i>
{% endif %}
</td>
<td class="col">{{ user.first_name }} {{ user.last_name }}</td>
<td class="col">{{ user.email }}</td>
<td class="col">
{% if user.is_superuser %}
<i class="fa-solid fa-solid fa-check text-success"></i>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<c-msg.empty title="{% translate "No users" %}" remove-padding></c-msg.empty>
{% endif %}
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,8 @@
{% extends "layouts/base.html" %}
{% load i18n %}
{% block title %}{% translate 'Users' %}{% endblock %}
{% block content %}
<div hx-get="{% url 'users_list' %}" hx-trigger="load, updated from:window" class="show-loading"></div>
{% endblock %}

View File

@@ -6,6 +6,7 @@ window.TomSelect = function createDynamicTomSelect(element) {
// Basic configuration
const config = {
plugins: {},
maxOptions: null,
// Extract 'create' option from data attribute
create: element.dataset.create === 'true',

View File

@@ -1,32 +1,32 @@
Django~=5.1
psycopg[binary]==3.2.3
python-webpack-boilerplate==1.0.3
psycopg[binary]==3.2.6
python-webpack-boilerplate==1.0.4
django-crispy-forms==2.3
crispy-bootstrap5==2024.10
django-browser-reload==1.12.1
django-hijack==3.4.5
django-filter==24.3
django-debug-toolbar==4.3.0
django-cachalot~=2.6.3
django-cotton~=1.2.1
crispy-bootstrap5==2025.4
django-browser-reload==1.18.0
django-hijack==3.7.1
django-filter==25.1
django-debug-toolbar==4.4.6
django-cachalot~=2.7.0
django-cotton~=1.5.2
django-pwa~=2.0.1
djangorestframework~=3.15.2
drf-spectacular~=0.27.2
django-import-export~=4.3.5
djangorestframework~=3.16.0
drf-spectacular~=0.28.0
django-import-export~=4.3.7
gunicorn==23.0.0
whitenoise[brotli]==6.6.0
whitenoise[brotli]==6.9.0
watchfiles==0.24.0 # https://github.com/samuelcolvin/watchfiles
procrastinate[django]~=2.14
procrastinate[django]~=2.15.1
requests~=2.32.3
pytz~=2024.2
pytz
python-dateutil~=2.9.0.post0
simpleeval~=1.0.0
pydantic~=2.10.5
simpleeval~=1.0.3
pydantic~=2.11.3
PyYAML~=6.0.2
mistune~=3.1.1
openpyxl~=3.1
xlrd~=2.0
mistune~=3.1.3
openpyxl~=3.1.5
xlrd~=2.0.1