Compare commits

...

78 Commits
0.18.3 ... main

Author SHA1 Message Date
Herculino Trotta
3e4d7c6b1f Merge pull request #497 from icovada/pagination_simplification
refactor(api): apply CustomNumberPagination to all API views
2026-01-11 13:58:57 -03:00
Herculino Trotta
63868514f9 Merge pull request #499 from eitchtee/weblate
Translations update from Weblate
2026-01-11 13:55:39 -03:00
Herculino Trotta
9055a24327 locale(Portuguese (Brazil)): update translation
Currently translated at 100.0% (717 of 717 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/pt_BR/
2026-01-11 16:55:26 +00:00
Dimitri Decrock
9dc963ed7b locale(Dutch): update translation
Currently translated at 100.0% (717 of 717 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/nl/
2026-01-11 13:24:30 +00:00
Herculino Trotta
49cac0588e add tests and fix missing get_queryset 2026-01-11 12:20:27 +01:00
icovada
3b2b6d6473 Query all DCA Strategies 2026-01-11 12:19:57 +01:00
icovada
db30bcbeb7 Remove filtering function superseesed by search_fields 2026-01-11 12:19:57 +01:00
icovada
a122733a47 Enable filtering and sorting on all API views 2026-01-11 12:19:30 +01:00
eitchtee
37f3e4d99a chore(locale): update translation files
[skip ci] Automatically generated by Django makemessages workflow
2026-01-10 20:50:08 +00:00
Herculino Trotta
d756286135 Merge pull request #496 from eitchtee/dev
feat(automatic-exchange-rate): track and display unsuccessful runs
2026-01-10 17:49:44 -03:00
Herculino Trotta
06a7378fd8 Merge pull request #491 from icovada/rest_filtering
feat(api): filtering
2026-01-10 17:46:04 -03:00
Herculino Trotta
ab4075c500 fix: missing list close 2026-01-10 17:44:57 -03:00
Herculino Trotta
96318f003d Merge branch 'main' into rest_filtering 2026-01-10 17:43:45 -03:00
Herculino Trotta
1a0412264a add tests and fix missing get_queryset 2026-01-10 17:42:37 -03:00
icovada
2588404876 Merge branch 'main' into pagination_simplification 2026-01-10 18:16:34 +01:00
Herculino Trotta
fdc273103b Merge pull request #485 from icovada/token_authentication
feat(api): add token authentication
2026-01-10 14:15:28 -03:00
icovada
c015b78cd6 Apply CustomNumberPagination to all API views 2026-01-10 17:14:53 +00:00
Herculino Trotta
50e5492ea1 feat(automatic-exchange-rate): track unsuccessful runs 2026-01-10 14:10:21 -03:00
eitchtee
796089cdb3 chore(locale): update translation files
[skip ci] Automatically generated by Django makemessages workflow
2026-01-10 05:54:02 +00:00
Herculino Trotta
c83b1bf2d6 Merge pull request #495 from eitchtee/dev
feat: add late section to monthly and all views (w/ default ordering)
2026-01-10 02:53:30 -03:00
Herculino Trotta
b074ef7929 feat: add late section to monthly and all views (w/ default ordering) 2026-01-10 02:52:46 -03:00
Herculino Trotta
ec7e33b3b0 Merge pull request #494 from eitchtee/weblate
Translations update from Weblate
2026-01-10 00:10:03 -03:00
Herculino Trotta
72fedea0db locale(Hungarian): update translation
Currently translated at 21.6% (155 of 715 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/hu/
2026-01-10 03:09:45 +00:00
Herculino Trotta
0a03745ce6 Merge pull request #493 from eitchtee/dev
fix(dca): strategy api endpoint returns nothing
2026-01-09 23:53:04 -03:00
Herculino Trotta
ff4bd79634 fix(dca): strategy api endpoint returns nothing 2026-01-09 23:51:31 -03:00
eitchtee
383b42e26d chore(locale): update translation files
[skip ci] Automatically generated by Django makemessages workflow
2026-01-10 02:27:25 +00:00
Herculino Trotta
48e43ac031 Merge pull request #492 from eitchtee/dev
fix(transactions): empty internal_id raises duplicate error when editing via django admin
2026-01-09 23:27:01 -03:00
Herculino Trotta
21c60c4059 Merge pull request #483 from eitchtee/weblate
Translations update from Weblate
2026-01-09 23:26:22 -03:00
Herculino Trotta
dd6a390e6b fix(transactions): empty internal_id raises duplicate error when editing via django admin 2026-01-09 23:25:13 -03:00
icovada
0c961a8250 Query all DCA Strategies 2026-01-08 22:51:50 +01:00
icovada
e28c651973 Remove filtering function superseesed by search_fields 2026-01-08 22:51:50 +01:00
icovada
7687ff81c3 Enable filtering and sorting on all API views 2026-01-08 22:51:49 +01:00
Janez
b2d78c9190 locale(Hungarian): update translation
Currently translated at 21.6% (155 of 715 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/hu/
2026-01-02 13:24:30 +00:00
icovada
b0815e00c7 Add token authentication to the API 2026-01-02 13:56:15 +01:00
Janez
fbe9726338 locale(Hungarian): update translation
Currently translated at 19.4% (139 of 715 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/hu/
2026-01-02 12:30:11 +00:00
Janez
0df3a57a33 locale(Hungarian): update translation
Currently translated at 17.6% (126 of 715 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/hu/
2026-01-02 12:24:31 +00:00
Janez
f86613b17a locale(Hungarian): update translation
Currently translated at 4.6% (33 of 715 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/hu/
2026-01-02 11:24:30 +00:00
Herculino Trotta
ffa4644e1b Merge pull request #482 from eitchtee/dev
fix(import_restore): unable to restore installment plans when there's multiple accounts with the same name
2025-12-30 22:00:33 -03:00
eitchtee
6611559696 chore(locale): update translation files
[skip ci] Automatically generated by Django makemessages workflow
2025-12-31 00:59:30 +00:00
Herculino Trotta
b455a0251a fix(import_restore): unable to restore installment plans when there's multiple accounts with the same name 2025-12-30 21:59:29 -03:00
Herculino Trotta
9d7c3212f1 Merge pull request #481 from eitchtee/dev
refactor: improve month by month and year by year value display
2025-12-30 21:59:04 -03:00
Herculino Trotta
0da3185996 Merge pull request #479 from eitchtee/weblate
Translations update from Weblate
2025-12-30 21:58:42 -03:00
Herculino Trotta
6c90e1bb7f refactor: improve month by month and year by year value display 2025-12-30 21:58:12 -03:00
Dimitri Decrock
c6543c0841 locale(Dutch): update translation
Currently translated at 100.0% (715 of 715 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/nl/
2025-12-29 07:24:30 +00:00
Herculino Trotta
d4740b8406 locale(Portuguese (Brazil)): update translation
Currently translated at 100.0% (715 of 715 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/pt_BR/
2025-12-29 02:24:30 +00:00
eitchtee
5a51795e6a chore(locale): update translation files
[skip ci] Automatically generated by Django makemessages workflow
2025-12-29 01:59:20 +00:00
Herculino Trotta
64d7765357 Merge pull request #478 from eitchtee/dev
feat(insights): new month by month insight
2025-12-28 22:58:56 -03:00
eitchtee
070e11ca77 chore(locale): update translation files
[skip ci] Automatically generated by Django makemessages workflow
2025-12-29 01:57:39 +00:00
Herculino Trotta
39f66b620a feat(insights): new month by month insight 2025-12-28 22:57:29 -03:00
Herculino Trotta
ad164866e0 Merge pull request #477 from eitchtee/dev
feat(insights): new year by year insight
2025-12-28 22:57:16 -03:00
Herculino Trotta
05c465cb34 Merge pull request #476 from eitchtee/weblate
Translations update from Weblate
2025-12-28 22:56:32 -03:00
Herculino Trotta
92cf526b76 feat(insights): new year by year insight 2025-12-28 22:55:58 -03:00
icovada
639236b890 locale(Italian): update translation
Currently translated at 99.4% (694 of 698 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/it/
2025-12-28 22:24:30 +00:00
Herculino Trotta
519a85d256 Merge pull request #474 from eitchtee/dev
feat(tests): add tests for monthly summaries
2025-12-28 13:36:54 -03:00
Herculino Trotta
700d35b5d5 feat(tests): add tests for monthly summaries 2025-12-28 13:36:21 -03:00
eitchtee
10e51971db chore(locale): update translation files
[skip ci] Automatically generated by Django makemessages workflow
2025-12-28 16:22:23 +00:00
Herculino Trotta
ec0d5fc121 Merge pull request #473 from eitchtee/dev
feat(transactions:filter): make monthly summary filter-aware
2025-12-28 13:21:55 -03:00
Herculino Trotta
01f91352d6 feat(transactions:filter): make montlhy summary filter-aware 2025-12-28 13:20:25 -03:00
eitchtee
63ce57a315 chore(locale): update translation files
[skip ci] Automatically generated by Django makemessages workflow
2025-12-28 16:11:03 +00:00
Herculino Trotta
eadeb649a1 Merge pull request #470 from eitchtee/dev
feat(transactions:filter): add filter for muted and unmuted transactions
2025-12-28 13:10:39 -03:00
Herculino Trotta
a2871d5289 feat(transactions:filter): add filter for muted and unmuted transactions 2025-12-28 13:09:41 -03:00
Herculino Trotta
f2a362bc0f Merge pull request #469 from eitchtee/dev
feat(app): add sanity checks for env variables & refactor: order management lists by name instead of id
2025-12-27 23:47:04 -03:00
Herculino Trotta
2076903740 refactor: order management lists by name instead of id 2025-12-27 23:43:57 -03:00
Herculino Trotta
c752c0b16e Merge pull request #468 from icovada/migrate-to-uv
Manage dependencies with `uv`
2025-12-27 20:18:20 -03:00
Herculino Trotta
1674766253 Merge pull request #467 from eitchtee/weblate
Translations update from Weblate
2025-12-27 20:18:00 -03:00
Herculino Trotta
7ea9d56132 docs: update uv.lock 2025-12-27 20:11:36 -03:00
Herculino Trotta
3699c6c671 docs: remove version from pyproject.yml 2025-12-27 19:58:46 -03:00
Herculino Trotta
d7c255aa14 refactor: remove build context from production image 2025-12-27 19:53:57 -03:00
Herculino Trotta
d17b9d5736 fix: dev image fails due to the environment being overwritten at runtime 2025-12-26 10:30:22 -03:00
Herculino Trotta
c7ff6db0bf feat(app): add sanity checks for env variables 2025-12-26 09:55:57 -03:00
Federico Tabbò
a4c7753f69 Configure setuptools to filter folders 2025-12-26 10:14:45 +01:00
icovada
7e08028557 use uv in GH actions 2025-12-21 17:28:45 +01:00
icovada
5eaf5086d2 build prod image with uv 2025-12-21 17:28:39 +01:00
icovada
c949c6cea0 add build instructions to prod docker-compose 2025-12-21 17:28:24 +01:00
icovada
71c0e9a271 locale(Italian): update translation
Currently translated at 99.5% (694 of 697 strings)

Translation: WYGIWYH/App
Translate-URL: https://translations.herculino.com/projects/wygiwyh/app/it/
2025-12-21 15:24:30 +00:00
icovada
bc65980511 migrate dev dockerfile to uv 2025-12-21 16:12:18 +01:00
icovada
ecdb1a52cc stop docker copying pycache in images 2025-12-21 16:10:50 +01:00
icovada
afc06582b4 set up dependencies in uv 2025-12-21 16:09:12 +01:00
67 changed files with 9060 additions and 2372 deletions

1
.dockerignore Normal file
View File

@@ -0,0 +1 @@
__pycache__/

View File

@@ -32,15 +32,16 @@ jobs:
token: ${{ secrets.PAT }}
ref: ${{ github.head_ref }}
- name: Set up Python 3.11
uses: actions/setup-python@v4
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
python-version: '3.11'
enable-cache: true
- name: Set up Python 3.11
run: uv python install 3.11
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
run: uv sync --frozen --no-dev
- name: Install gettext
run: sudo apt-get install -y gettext
@@ -48,7 +49,7 @@ jobs:
- name: Run makemessages
run: |
cd app
python manage.py makemessages -a
uv run python manage.py makemessages -a
- name: Check for changes
id: check_changes

View File

@@ -70,6 +70,7 @@ INSTALLED_APPS = [
"apps.api.apps.ApiConfig",
"cachalot",
"rest_framework",
"rest_framework.authtoken",
"drf_spectacular",
"django_cotton",
"apps.rules.apps.RulesConfig",
@@ -433,8 +434,16 @@ REST_FRAMEWORK = {
"apps.api.permissions.NotInDemoMode",
"rest_framework.permissions.DjangoModelPermissions",
],
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
"PAGE_SIZE": 10,
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend',
'rest_framework.filters.OrderingFilter',
],
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
],
"DEFAULT_PAGINATION_CLASS": "apps.api.custom.pagination.CustomPageNumberPagination",
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
}

View File

@@ -25,7 +25,7 @@ def account_groups_index(request):
@login_required
@require_http_methods(["GET"])
def account_groups_list(request):
account_groups = AccountGroup.objects.all().order_by("id")
account_groups = AccountGroup.objects.all().order_by("name")
return render(
request,
"account_groups/fragments/list.html",

View File

@@ -25,7 +25,7 @@ def accounts_index(request):
@login_required
@require_http_methods(["GET"])
def accounts_list(request):
accounts = Account.objects.all().order_by("id")
accounts = Account.objects.all().order_by("name")
return render(
request,
"accounts/fragments/list.html",

View File

@@ -1,4 +1,5 @@
# Import all test classes for Django test discovery
from .test_imports import *
from .test_accounts import *
from .test_data_isolation import *
from .test_shared_access import *

View File

@@ -0,0 +1,719 @@
from datetime import date
from decimal import Decimal
from django.contrib.auth import get_user_model
from django.test import TestCase, override_settings
from rest_framework import status
from rest_framework.test import APIClient
from apps.accounts.models import Account, AccountGroup
from apps.currencies.models import Currency
from apps.dca.models import DCAStrategy, DCAEntry
from apps.transactions.models import (
Transaction,
TransactionCategory,
TransactionTag,
TransactionEntity,
InstallmentPlan,
RecurringTransaction,
)
ACCESS_DENIED_CODES = [status.HTTP_403_FORBIDDEN, status.HTTP_404_NOT_FOUND]
@override_settings(
STORAGES={
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"
},
},
WHITENOISE_AUTOREFRESH=True,
)
class AccountDataIsolationTests(TestCase):
"""Tests to ensure users cannot access other users' accounts."""
def setUp(self):
"""Set up test data with two distinct users."""
User = get_user_model()
# User 1 - the requester
self.user1 = User.objects.create_user(
email="user1@test.com", password="testpass123"
)
self.client1 = APIClient()
self.client1.force_authenticate(user=self.user1)
# User 2 - owner of data that user1 should NOT access
self.user2 = User.objects.create_user(
email="user2@test.com", password="testpass123"
)
self.client2 = APIClient()
self.client2.force_authenticate(user=self.user2)
# Shared currency
self.currency = Currency.objects.create(
code="USD", name="US Dollar", decimal_places=2, prefix="$ "
)
# User 1's account
self.user1_account_group = AccountGroup.all_objects.create(
name="User1 Group", owner=self.user1
)
self.user1_account = Account.all_objects.create(
name="User1 Account",
group=self.user1_account_group,
currency=self.currency,
owner=self.user1,
)
# User 2's account (private, should be invisible to user1)
self.user2_account_group = AccountGroup.all_objects.create(
name="User2 Group", owner=self.user2
)
self.user2_account = Account.all_objects.create(
name="User2 Account",
group=self.user2_account_group,
currency=self.currency,
owner=self.user2,
)
def test_user_cannot_see_other_users_accounts_in_list(self):
"""GET /api/accounts/ should only return user's own accounts."""
response = self.client1.get("/api/accounts/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
# User1 should only see their own account
account_ids = [acc["id"] for acc in response.data["results"]]
self.assertIn(self.user1_account.id, account_ids)
self.assertNotIn(self.user2_account.id, account_ids)
def test_user_cannot_access_other_users_account_detail(self):
"""GET /api/accounts/{id}/ should deny access to other user's account."""
response = self.client1.get(f"/api/accounts/{self.user2_account.id}/")
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
def test_user_cannot_modify_other_users_account(self):
"""PATCH on other user's account should deny access."""
response = self.client1.patch(
f"/api/accounts/{self.user2_account.id}/",
{"name": "Hacked Account"},
)
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
# Verify account name wasn't changed
self.user2_account.refresh_from_db()
self.assertEqual(self.user2_account.name, "User2 Account")
def test_user_cannot_delete_other_users_account(self):
"""DELETE on other user's account should deny access."""
response = self.client1.delete(f"/api/accounts/{self.user2_account.id}/")
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
# Verify account still exists
self.assertTrue(Account.all_objects.filter(id=self.user2_account.id).exists())
def test_user_cannot_get_balance_of_other_users_account(self):
"""Balance action on other user's account should deny access."""
response = self.client1.get(f"/api/accounts/{self.user2_account.id}/balance/")
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
def test_user_can_access_own_account(self):
"""User can access their own account normally."""
response = self.client1.get(f"/api/accounts/{self.user1_account.id}/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["name"], "User1 Account")
@override_settings(
STORAGES={
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"
},
},
WHITENOISE_AUTOREFRESH=True,
)
class AccountGroupDataIsolationTests(TestCase):
"""Tests to ensure users cannot access other users' account groups."""
def setUp(self):
"""Set up test data with two distinct users."""
User = get_user_model()
self.user1 = User.objects.create_user(
email="user1@test.com", password="testpass123"
)
self.client1 = APIClient()
self.client1.force_authenticate(user=self.user1)
self.user2 = User.objects.create_user(
email="user2@test.com", password="testpass123"
)
# User 1's account group
self.user1_group = AccountGroup.all_objects.create(
name="User1 Group", owner=self.user1
)
# User 2's account group
self.user2_group = AccountGroup.all_objects.create(
name="User2 Group", owner=self.user2
)
def test_user_cannot_see_other_users_account_groups(self):
"""GET /api/account-groups/ should only return user's own groups."""
response = self.client1.get("/api/account-groups/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
group_ids = [grp["id"] for grp in response.data["results"]]
self.assertIn(self.user1_group.id, group_ids)
self.assertNotIn(self.user2_group.id, group_ids)
def test_user_cannot_access_other_users_account_group_detail(self):
"""GET /api/account-groups/{id}/ should deny access to other user's group."""
response = self.client1.get(f"/api/account-groups/{self.user2_group.id}/")
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
def test_user_cannot_modify_other_users_account_group(self):
"""PATCH on other user's account group should deny access."""
response = self.client1.patch(
f"/api/account-groups/{self.user2_group.id}/",
{"name": "Hacked Group"},
)
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
self.user2_group.refresh_from_db()
self.assertEqual(self.user2_group.name, "User2 Group")
def test_user_cannot_delete_other_users_account_group(self):
"""DELETE on other user's account group should deny access."""
response = self.client1.delete(f"/api/account-groups/{self.user2_group.id}/")
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
self.assertTrue(
AccountGroup.all_objects.filter(id=self.user2_group.id).exists()
)
@override_settings(
STORAGES={
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"
},
},
WHITENOISE_AUTOREFRESH=True,
)
class TransactionDataIsolationTests(TestCase):
"""Tests to ensure users cannot access other users' transactions."""
def setUp(self):
"""Set up test data with transactions for two distinct users."""
User = get_user_model()
self.user1 = User.objects.create_user(
email="user1@test.com", password="testpass123"
)
self.client1 = APIClient()
self.client1.force_authenticate(user=self.user1)
self.user2 = User.objects.create_user(
email="user2@test.com", password="testpass123"
)
self.currency = Currency.objects.create(
code="USD", name="US Dollar", decimal_places=2, prefix="$ "
)
# User 1's account and transaction
self.user1_account = Account.all_objects.create(
name="User1 Account", currency=self.currency, owner=self.user1
)
self.user1_transaction = Transaction.userless_all_objects.create(
account=self.user1_account,
type=Transaction.Type.INCOME,
amount=Decimal("100.00"),
is_paid=True,
date=date(2025, 1, 1),
description="User1 Income",
owner=self.user1,
)
# User 2's account and transaction
self.user2_account = Account.all_objects.create(
name="User2 Account", currency=self.currency, owner=self.user2
)
self.user2_transaction = Transaction.userless_all_objects.create(
account=self.user2_account,
type=Transaction.Type.EXPENSE,
amount=Decimal("50.00"),
is_paid=True,
date=date(2025, 1, 1),
description="User2 Expense",
owner=self.user2,
)
def test_user_cannot_see_other_users_transactions_in_list(self):
"""GET /api/transactions/ should only return user's own transactions."""
response = self.client1.get("/api/transactions/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
transaction_ids = [t["id"] for t in response.data["results"]]
self.assertIn(self.user1_transaction.id, transaction_ids)
self.assertNotIn(self.user2_transaction.id, transaction_ids)
def test_user_cannot_access_other_users_transaction_detail(self):
"""GET /api/transactions/{id}/ should deny access to other user's transaction."""
response = self.client1.get(f"/api/transactions/{self.user2_transaction.id}/")
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
def test_user_cannot_modify_other_users_transaction(self):
"""PATCH on other user's transaction should deny access."""
response = self.client1.patch(
f"/api/transactions/{self.user2_transaction.id}/",
{"description": "Hacked Transaction"},
)
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
self.user2_transaction.refresh_from_db()
self.assertEqual(self.user2_transaction.description, "User2 Expense")
def test_user_cannot_delete_other_users_transaction(self):
"""DELETE on other user's transaction should deny access."""
response = self.client1.delete(
f"/api/transactions/{self.user2_transaction.id}/"
)
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
self.assertTrue(
Transaction.userless_all_objects.filter(
id=self.user2_transaction.id
).exists()
)
def test_user_cannot_create_transaction_in_other_users_account(self):
"""POST /api/transactions/ with other user's account should fail."""
response = self.client1.post(
"/api/transactions/",
{
"account": self.user2_account.id,
"type": "IN",
"amount": "100.00",
"date": "2025-01-15",
"description": "Sneaky transaction",
},
format="json",
)
# Should deny access - 400 (validation error), 403, or 404
self.assertIn(
response.status_code,
ACCESS_DENIED_CODES + [status.HTTP_400_BAD_REQUEST],
)
@override_settings(
STORAGES={
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"
},
},
WHITENOISE_AUTOREFRESH=True,
)
class CategoryTagEntityIsolationTests(TestCase):
"""Tests for isolation of categories, tags, and entities between users."""
def setUp(self):
"""Set up test data."""
User = get_user_model()
self.user1 = User.objects.create_user(
email="user1@test.com", password="testpass123"
)
self.client1 = APIClient()
self.client1.force_authenticate(user=self.user1)
self.user2 = User.objects.create_user(
email="user2@test.com", password="testpass123"
)
# User 1's categories, tags, entities
self.user1_category = TransactionCategory.all_objects.create(
name="User1 Category", owner=self.user1
)
self.user1_tag = TransactionTag.all_objects.create(
name="User1 Tag", owner=self.user1
)
self.user1_entity = TransactionEntity.all_objects.create(
name="User1 Entity", owner=self.user1
)
# User 2's categories, tags, entities
self.user2_category = TransactionCategory.all_objects.create(
name="User2 Category", owner=self.user2
)
self.user2_tag = TransactionTag.all_objects.create(
name="User2 Tag", owner=self.user2
)
self.user2_entity = TransactionEntity.all_objects.create(
name="User2 Entity", owner=self.user2
)
def test_user_cannot_see_other_users_categories(self):
"""GET /api/categories/ should only return user's own categories."""
response = self.client1.get("/api/categories/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
category_ids = [c["id"] for c in response.data["results"]]
self.assertIn(self.user1_category.id, category_ids)
self.assertNotIn(self.user2_category.id, category_ids)
def test_user_cannot_access_other_users_category_detail(self):
"""GET /api/categories/{id}/ should deny access to other user's category."""
response = self.client1.get(f"/api/categories/{self.user2_category.id}/")
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
def test_user_cannot_see_other_users_tags(self):
"""GET /api/tags/ should only return user's own tags."""
response = self.client1.get("/api/tags/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
tag_ids = [t["id"] for t in response.data["results"]]
self.assertIn(self.user1_tag.id, tag_ids)
self.assertNotIn(self.user2_tag.id, tag_ids)
def test_user_cannot_access_other_users_tag_detail(self):
"""GET /api/tags/{id}/ should deny access to other user's tag."""
response = self.client1.get(f"/api/tags/{self.user2_tag.id}/")
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
def test_user_cannot_see_other_users_entities(self):
"""GET /api/entities/ should only return user's own entities."""
response = self.client1.get("/api/entities/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
entity_ids = [e["id"] for e in response.data["results"]]
self.assertIn(self.user1_entity.id, entity_ids)
self.assertNotIn(self.user2_entity.id, entity_ids)
def test_user_cannot_access_other_users_entity_detail(self):
"""GET /api/entities/{id}/ should deny access to other user's entity."""
response = self.client1.get(f"/api/entities/{self.user2_entity.id}/")
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
def test_user_cannot_modify_other_users_category(self):
"""PATCH on other user's category should deny access."""
response = self.client1.patch(
f"/api/categories/{self.user2_category.id}/",
{"name": "Hacked Category"},
)
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
def test_user_cannot_delete_other_users_tag(self):
"""DELETE on other user's tag should deny access."""
response = self.client1.delete(f"/api/tags/{self.user2_tag.id}/")
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
self.assertTrue(
TransactionTag.all_objects.filter(id=self.user2_tag.id).exists()
)
@override_settings(
STORAGES={
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"
},
},
WHITENOISE_AUTOREFRESH=True,
)
class DCADataIsolationTests(TestCase):
"""Tests to ensure users cannot access other users' DCA strategies and entries."""
def setUp(self):
"""Set up test data."""
User = get_user_model()
self.user1 = User.objects.create_user(
email="user1@test.com", password="testpass123"
)
self.client1 = APIClient()
self.client1.force_authenticate(user=self.user1)
self.user2 = User.objects.create_user(
email="user2@test.com", password="testpass123"
)
self.currency1 = Currency.objects.create(
code="BTC", name="Bitcoin", decimal_places=8, prefix=""
)
self.currency2 = Currency.objects.create(
code="USD", name="US Dollar", decimal_places=2, prefix="$ "
)
# User 1's DCA strategy and entry
self.user1_strategy = DCAStrategy.all_objects.create(
name="User1 BTC Strategy",
target_currency=self.currency1,
payment_currency=self.currency2,
owner=self.user1,
)
self.user1_entry = DCAEntry.objects.create(
strategy=self.user1_strategy,
date=date(2025, 1, 1),
amount_paid=Decimal("100.00"),
amount_received=Decimal("0.001"),
)
# User 2's DCA strategy and entry
self.user2_strategy = DCAStrategy.all_objects.create(
name="User2 BTC Strategy",
target_currency=self.currency1,
payment_currency=self.currency2,
owner=self.user2,
)
self.user2_entry = DCAEntry.objects.create(
strategy=self.user2_strategy,
date=date(2025, 1, 1),
amount_paid=Decimal("200.00"),
amount_received=Decimal("0.002"),
)
def test_user_cannot_see_other_users_dca_strategies(self):
"""GET /api/dca/strategies/ should only return user's own strategies."""
response = self.client1.get("/api/dca/strategies/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
strategy_ids = [s["id"] for s in response.data["results"]]
self.assertIn(self.user1_strategy.id, strategy_ids)
self.assertNotIn(self.user2_strategy.id, strategy_ids)
def test_user_cannot_access_other_users_dca_strategy_detail(self):
"""GET /api/dca/strategies/{id}/ should deny access to other user's strategy."""
response = self.client1.get(f"/api/dca/strategies/{self.user2_strategy.id}/")
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
def test_user_cannot_access_other_users_dca_entries(self):
"""GET /api/dca/entries/ filtered by other user's strategy should return empty."""
response = self.client1.get(
f"/api/dca/entries/?strategy={self.user2_strategy.id}"
)
# Either OK with empty results or error
if response.status_code == status.HTTP_200_OK:
entry_ids = [e["id"] for e in response.data["results"]]
self.assertNotIn(self.user2_entry.id, entry_ids)
def test_user_cannot_access_other_users_dca_entry_detail(self):
"""GET /api/dca/entries/{id}/ should deny access to other user's entry."""
response = self.client1.get(f"/api/dca/entries/{self.user2_entry.id}/")
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
def test_user_cannot_access_other_users_strategy_investment_frequency(self):
"""investment_frequency action on other user's strategy should deny access."""
response = self.client1.get(
f"/api/dca/strategies/{self.user2_strategy.id}/investment_frequency/"
)
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
def test_user_cannot_access_other_users_strategy_price_comparison(self):
"""price_comparison action on other user's strategy should deny access."""
response = self.client1.get(
f"/api/dca/strategies/{self.user2_strategy.id}/price_comparison/"
)
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
def test_user_cannot_access_other_users_strategy_current_price(self):
"""current_price action on other user's strategy should deny access."""
response = self.client1.get(
f"/api/dca/strategies/{self.user2_strategy.id}/current_price/"
)
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
def test_user_cannot_modify_other_users_dca_strategy(self):
"""PATCH on other user's DCA strategy should deny access."""
response = self.client1.patch(
f"/api/dca/strategies/{self.user2_strategy.id}/",
{"name": "Hacked Strategy"},
)
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
def test_user_cannot_delete_other_users_dca_entry(self):
"""DELETE on other user's DCA entry should deny access."""
response = self.client1.delete(f"/api/dca/entries/{self.user2_entry.id}/")
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
self.assertTrue(DCAEntry.objects.filter(id=self.user2_entry.id).exists())
@override_settings(
STORAGES={
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"
},
},
WHITENOISE_AUTOREFRESH=True,
)
class InstallmentRecurringIsolationTests(TestCase):
"""Tests for isolation of installment plans and recurring transactions."""
def setUp(self):
"""Set up test data."""
User = get_user_model()
self.user1 = User.objects.create_user(
email="user1@test.com", password="testpass123"
)
self.client1 = APIClient()
self.client1.force_authenticate(user=self.user1)
self.user2 = User.objects.create_user(
email="user2@test.com", password="testpass123"
)
self.currency = Currency.objects.create(
code="USD", name="US Dollar", decimal_places=2, prefix="$ "
)
# User 1's account
self.user1_account = Account.all_objects.create(
name="User1 Account", currency=self.currency, owner=self.user1
)
# User 2's account
self.user2_account = Account.all_objects.create(
name="User2 Account", currency=self.currency, owner=self.user2
)
# User 1's installment plan
self.user1_installment = InstallmentPlan.all_objects.create(
account=self.user1_account,
type=Transaction.Type.EXPENSE,
description="User1 Installment",
number_of_installments=12,
start_date=date(2025, 1, 1),
installment_amount=Decimal("100.00"),
)
# User 2's installment plan
self.user2_installment = InstallmentPlan.all_objects.create(
account=self.user2_account,
type=Transaction.Type.EXPENSE,
description="User2 Installment",
number_of_installments=6,
start_date=date(2025, 1, 1),
installment_amount=Decimal("200.00"),
)
# User 1's recurring transaction
self.user1_recurring = RecurringTransaction.all_objects.create(
account=self.user1_account,
type=Transaction.Type.EXPENSE,
amount=Decimal("50.00"),
description="User1 Recurring",
start_date=date(2025, 1, 1),
recurrence_type=RecurringTransaction.RecurrenceType.MONTH,
recurrence_interval=1,
)
# User 2's recurring transaction
self.user2_recurring = RecurringTransaction.all_objects.create(
account=self.user2_account,
type=Transaction.Type.INCOME,
amount=Decimal("1000.00"),
description="User2 Recurring",
start_date=date(2025, 1, 1),
recurrence_type=RecurringTransaction.RecurrenceType.MONTH,
recurrence_interval=1,
)
def test_user_cannot_see_other_users_installment_plans(self):
"""GET /api/installment-plans/ should only return user's own plans."""
response = self.client1.get("/api/installment-plans/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
plan_ids = [p["id"] for p in response.data["results"]]
self.assertIn(self.user1_installment.id, plan_ids)
self.assertNotIn(self.user2_installment.id, plan_ids)
def test_user_cannot_access_other_users_installment_plan_detail(self):
"""GET /api/installment-plans/{id}/ should deny access to other user's plan."""
response = self.client1.get(
f"/api/installment-plans/{self.user2_installment.id}/"
)
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
def test_user_cannot_see_other_users_recurring_transactions(self):
"""GET /api/recurring-transactions/ should only return user's own recurring."""
response = self.client1.get("/api/recurring-transactions/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
recurring_ids = [r["id"] for r in response.data["results"]]
self.assertIn(self.user1_recurring.id, recurring_ids)
self.assertNotIn(self.user2_recurring.id, recurring_ids)
def test_user_cannot_access_other_users_recurring_transaction_detail(self):
"""GET /api/recurring-transactions/{id}/ should deny access to other user's recurring."""
response = self.client1.get(
f"/api/recurring-transactions/{self.user2_recurring.id}/"
)
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
def test_user_cannot_modify_other_users_installment_plan(self):
"""PATCH on other user's installment plan should deny access."""
response = self.client1.patch(
f"/api/installment-plans/{self.user2_installment.id}/",
{"description": "Hacked Installment"},
)
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
def test_user_cannot_delete_other_users_recurring_transaction(self):
"""DELETE on other user's recurring transaction should deny access."""
response = self.client1.delete(
f"/api/recurring-transactions/{self.user2_recurring.id}/"
)
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
self.assertTrue(
RecurringTransaction.all_objects.filter(id=self.user2_recurring.id).exists()
)

View File

@@ -0,0 +1,587 @@
from datetime import date
from decimal import Decimal
from django.contrib.auth import get_user_model
from django.test import TestCase, override_settings
from rest_framework import status
from rest_framework.test import APIClient
from apps.accounts.models import Account, AccountGroup
from apps.currencies.models import Currency
from apps.dca.models import DCAStrategy, DCAEntry
from apps.transactions.models import (
Transaction,
TransactionCategory,
TransactionTag,
TransactionEntity,
)
ACCESS_DENIED_CODES = [status.HTTP_403_FORBIDDEN, status.HTTP_404_NOT_FOUND]
@override_settings(
STORAGES={
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"
},
},
WHITENOISE_AUTOREFRESH=True,
)
class SharedAccountAccessTests(TestCase):
"""Tests for shared account access via shared_with field."""
def setUp(self):
"""Set up test data with shared accounts."""
User = get_user_model()
# User 1 - owner
self.user1 = User.objects.create_user(
email="user1@test.com", password="testpass123"
)
self.client1 = APIClient()
self.client1.force_authenticate(user=self.user1)
# User 2 - will have shared access
self.user2 = User.objects.create_user(
email="user2@test.com", password="testpass123"
)
self.client2 = APIClient()
self.client2.force_authenticate(user=self.user2)
# User 3 - no shared access
self.user3 = User.objects.create_user(
email="user3@test.com", password="testpass123"
)
self.client3 = APIClient()
self.client3.force_authenticate(user=self.user3)
self.currency = Currency.objects.create(
code="USD", name="US Dollar", decimal_places=2, prefix="$ "
)
# User 1's account shared with user 2
self.shared_account = Account.all_objects.create(
name="Shared Account",
currency=self.currency,
owner=self.user1,
visibility="private",
)
self.shared_account.shared_with.add(self.user2)
# User 1's private account (not shared)
self.private_account = Account.all_objects.create(
name="Private Account",
currency=self.currency,
owner=self.user1,
visibility="private",
)
# Transaction in shared account
self.shared_transaction = Transaction.userless_all_objects.create(
account=self.shared_account,
type=Transaction.Type.INCOME,
amount=Decimal("100.00"),
is_paid=True,
date=date(2025, 1, 1),
description="Shared Transaction",
owner=self.user1,
)
# Transaction in private account
self.private_transaction = Transaction.userless_all_objects.create(
account=self.private_account,
type=Transaction.Type.EXPENSE,
amount=Decimal("50.00"),
is_paid=True,
date=date(2025, 1, 1),
description="Private Transaction",
owner=self.user1,
)
def test_user_can_see_accounts_shared_with_them(self):
"""User2 should see the account shared with them."""
response = self.client2.get("/api/accounts/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
account_ids = [acc["id"] for acc in response.data["results"]]
self.assertIn(self.shared_account.id, account_ids)
def test_user_cannot_see_accounts_not_shared_with_them(self):
"""User2 should NOT see user1's private (non-shared) account."""
response = self.client2.get("/api/accounts/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
account_ids = [acc["id"] for acc in response.data["results"]]
self.assertNotIn(self.private_account.id, account_ids)
def test_user_can_access_shared_account_detail(self):
"""User2 should be able to access shared account details."""
response = self.client2.get(f"/api/accounts/{self.shared_account.id}/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["name"], "Shared Account")
def test_user_without_share_cannot_access_shared_account(self):
"""User3 should NOT be able to access the shared account."""
response = self.client3.get(f"/api/accounts/{self.shared_account.id}/")
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
def test_user_can_see_transactions_in_shared_account(self):
"""User2 should see transactions in the shared account."""
response = self.client2.get("/api/transactions/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
transaction_ids = [t["id"] for t in response.data["results"]]
self.assertIn(self.shared_transaction.id, transaction_ids)
self.assertNotIn(self.private_transaction.id, transaction_ids)
def test_user_can_access_transaction_in_shared_account(self):
"""User2 should be able to access transaction details in shared account."""
response = self.client2.get(f"/api/transactions/{self.shared_transaction.id}/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["description"], "Shared Transaction")
def test_user_cannot_access_transaction_in_non_shared_account(self):
"""User2 should NOT access transactions in user1's private account."""
response = self.client2.get(f"/api/transactions/{self.private_transaction.id}/")
self.assertIn(response.status_code, ACCESS_DENIED_CODES)
def test_user_can_get_balance_of_shared_account(self):
"""User2 should be able to get balance of shared account."""
response = self.client2.get(f"/api/accounts/{self.shared_account.id}/balance/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn("current_balance", response.data)
def test_sharing_works_with_multiple_users(self):
"""Account shared with multiple users should be accessible by all."""
# Add user3 to shared_with
self.shared_account.shared_with.add(self.user3)
# User2 still has access
response2 = self.client2.get(f"/api/accounts/{self.shared_account.id}/")
self.assertEqual(response2.status_code, status.HTTP_200_OK)
# User3 now has access
response3 = self.client3.get(f"/api/accounts/{self.shared_account.id}/")
self.assertEqual(response3.status_code, status.HTTP_200_OK)
@override_settings(
STORAGES={
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"
},
},
WHITENOISE_AUTOREFRESH=True,
)
class PublicVisibilityTests(TestCase):
"""Tests for public visibility access."""
def setUp(self):
"""Set up test data with public accounts."""
User = get_user_model()
self.user1 = User.objects.create_user(
email="user1@test.com", password="testpass123"
)
self.client1 = APIClient()
self.client1.force_authenticate(user=self.user1)
self.user2 = User.objects.create_user(
email="user2@test.com", password="testpass123"
)
self.client2 = APIClient()
self.client2.force_authenticate(user=self.user2)
self.currency = Currency.objects.create(
code="USD", name="US Dollar", decimal_places=2, prefix="$ "
)
# User 1's public account
self.public_account = Account.all_objects.create(
name="Public Account",
currency=self.currency,
owner=self.user1,
visibility="public",
)
# User 1's private account
self.private_account = Account.all_objects.create(
name="Private Account",
currency=self.currency,
owner=self.user1,
visibility="private",
)
# Transaction in public account
self.public_transaction = Transaction.userless_all_objects.create(
account=self.public_account,
type=Transaction.Type.INCOME,
amount=Decimal("100.00"),
is_paid=True,
date=date(2025, 1, 1),
description="Public Transaction",
owner=self.user1,
)
def test_user_can_see_public_accounts(self):
"""User2 should see user1's public account."""
response = self.client2.get("/api/accounts/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
account_ids = [acc["id"] for acc in response.data["results"]]
self.assertIn(self.public_account.id, account_ids)
self.assertNotIn(self.private_account.id, account_ids)
def test_user_can_access_public_account_detail(self):
"""User2 should be able to access public account details."""
response = self.client2.get(f"/api/accounts/{self.public_account.id}/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["name"], "Public Account")
def test_user_can_see_transactions_in_public_accounts(self):
"""User2 should see transactions in public accounts."""
response = self.client2.get("/api/transactions/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
transaction_ids = [t["id"] for t in response.data["results"]]
self.assertIn(self.public_transaction.id, transaction_ids)
@override_settings(
STORAGES={
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"
},
},
WHITENOISE_AUTOREFRESH=True,
)
class SharedCategoryTagEntityTests(TestCase):
"""Tests for shared categories, tags, and entities."""
def setUp(self):
"""Set up test data with shared categories/tags/entities."""
User = get_user_model()
self.user1 = User.objects.create_user(
email="user1@test.com", password="testpass123"
)
self.client1 = APIClient()
self.client1.force_authenticate(user=self.user1)
self.user2 = User.objects.create_user(
email="user2@test.com", password="testpass123"
)
self.client2 = APIClient()
self.client2.force_authenticate(user=self.user2)
self.user3 = User.objects.create_user(
email="user3@test.com", password="testpass123"
)
self.client3 = APIClient()
self.client3.force_authenticate(user=self.user3)
# User 1's category shared with user 2
self.shared_category = TransactionCategory.all_objects.create(
name="Shared Category", owner=self.user1
)
self.shared_category.shared_with.add(self.user2)
# User 1's private category
self.private_category = TransactionCategory.all_objects.create(
name="Private Category", owner=self.user1
)
# User 1's public category
self.public_category = TransactionCategory.all_objects.create(
name="Public Category", owner=self.user1, visibility="public"
)
# User 1's tag shared with user 2
self.shared_tag = TransactionTag.all_objects.create(
name="Shared Tag", owner=self.user1
)
self.shared_tag.shared_with.add(self.user2)
# User 1's entity shared with user 2
self.shared_entity = TransactionEntity.all_objects.create(
name="Shared Entity", owner=self.user1
)
self.shared_entity.shared_with.add(self.user2)
def test_user_can_see_shared_categories(self):
"""User2 should see categories shared with them."""
response = self.client2.get("/api/categories/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
category_ids = [c["id"] for c in response.data["results"]]
self.assertIn(self.shared_category.id, category_ids)
self.assertNotIn(self.private_category.id, category_ids)
def test_user_can_access_shared_category_detail(self):
"""User2 should be able to access shared category details."""
response = self.client2.get(f"/api/categories/{self.shared_category.id}/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["name"], "Shared Category")
def test_user_can_see_public_categories(self):
"""User3 should see public categories."""
response = self.client3.get("/api/categories/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
category_ids = [c["id"] for c in response.data["results"]]
self.assertIn(self.public_category.id, category_ids)
def test_user_without_share_cannot_see_shared_category(self):
"""User3 should NOT see category shared only with user2."""
response = self.client3.get("/api/categories/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
category_ids = [c["id"] for c in response.data["results"]]
self.assertNotIn(self.shared_category.id, category_ids)
def test_user_can_see_shared_tags(self):
"""User2 should see tags shared with them."""
response = self.client2.get("/api/tags/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
tag_ids = [t["id"] for t in response.data["results"]]
self.assertIn(self.shared_tag.id, tag_ids)
def test_user_can_access_shared_tag_detail(self):
"""User2 should be able to access shared tag details."""
response = self.client2.get(f"/api/tags/{self.shared_tag.id}/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["name"], "Shared Tag")
def test_user_can_see_shared_entities(self):
"""User2 should see entities shared with them."""
response = self.client2.get("/api/entities/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
entity_ids = [e["id"] for e in response.data["results"]]
self.assertIn(self.shared_entity.id, entity_ids)
def test_user_can_access_shared_entity_detail(self):
"""User2 should be able to access shared entity details."""
response = self.client2.get(f"/api/entities/{self.shared_entity.id}/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["name"], "Shared Entity")
@override_settings(
STORAGES={
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"
},
},
WHITENOISE_AUTOREFRESH=True,
)
class SharedDCAAccessTests(TestCase):
"""Tests for shared DCA strategy access."""
def setUp(self):
"""Set up test data with shared DCA strategies."""
User = get_user_model()
self.user1 = User.objects.create_user(
email="user1@test.com", password="testpass123"
)
self.client1 = APIClient()
self.client1.force_authenticate(user=self.user1)
self.user2 = User.objects.create_user(
email="user2@test.com", password="testpass123"
)
self.client2 = APIClient()
self.client2.force_authenticate(user=self.user2)
self.user3 = User.objects.create_user(
email="user3@test.com", password="testpass123"
)
self.client3 = APIClient()
self.client3.force_authenticate(user=self.user3)
self.currency1 = Currency.objects.create(
code="BTC", name="Bitcoin", decimal_places=8, prefix=""
)
self.currency2 = Currency.objects.create(
code="USD", name="US Dollar", decimal_places=2, prefix="$ "
)
# User 1's DCA strategy shared with user 2
self.shared_strategy = DCAStrategy.all_objects.create(
name="Shared BTC Strategy",
target_currency=self.currency1,
payment_currency=self.currency2,
owner=self.user1,
)
self.shared_strategy.shared_with.add(self.user2)
# Entry in shared strategy
self.shared_entry = DCAEntry.objects.create(
strategy=self.shared_strategy,
date=date(2025, 1, 1),
amount_paid=Decimal("100.00"),
amount_received=Decimal("0.001"),
)
# User 1's private strategy
self.private_strategy = DCAStrategy.all_objects.create(
name="Private BTC Strategy",
target_currency=self.currency1,
payment_currency=self.currency2,
owner=self.user1,
)
def test_user_can_see_shared_dca_strategies(self):
"""User2 should see DCA strategies shared with them."""
response = self.client2.get("/api/dca/strategies/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
strategy_ids = [s["id"] for s in response.data["results"]]
self.assertIn(self.shared_strategy.id, strategy_ids)
self.assertNotIn(self.private_strategy.id, strategy_ids)
def test_user_can_access_shared_dca_strategy_detail(self):
"""User2 should be able to access shared strategy details."""
response = self.client2.get(f"/api/dca/strategies/{self.shared_strategy.id}/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["name"], "Shared BTC Strategy")
def test_user_without_share_cannot_see_shared_strategy(self):
"""User3 should NOT see strategy shared only with user2."""
response = self.client3.get("/api/dca/strategies/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
strategy_ids = [s["id"] for s in response.data["results"]]
self.assertNotIn(self.shared_strategy.id, strategy_ids)
def test_user_can_access_shared_strategy_actions(self):
"""User2 should be able to access actions on shared strategy."""
# investment_frequency
response1 = self.client2.get(
f"/api/dca/strategies/{self.shared_strategy.id}/investment_frequency/"
)
self.assertEqual(response1.status_code, status.HTTP_200_OK)
# price_comparison
response2 = self.client2.get(
f"/api/dca/strategies/{self.shared_strategy.id}/price_comparison/"
)
self.assertEqual(response2.status_code, status.HTTP_200_OK)
# current_price
response3 = self.client2.get(
f"/api/dca/strategies/{self.shared_strategy.id}/current_price/"
)
self.assertEqual(response3.status_code, status.HTTP_200_OK)
@override_settings(
STORAGES={
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"
},
},
WHITENOISE_AUTOREFRESH=True,
)
class SharedAccountGroupTests(TestCase):
"""Tests for shared account group access."""
def setUp(self):
"""Set up test data with shared account groups."""
User = get_user_model()
self.user1 = User.objects.create_user(
email="user1@test.com", password="testpass123"
)
self.client1 = APIClient()
self.client1.force_authenticate(user=self.user1)
self.user2 = User.objects.create_user(
email="user2@test.com", password="testpass123"
)
self.client2 = APIClient()
self.client2.force_authenticate(user=self.user2)
self.user3 = User.objects.create_user(
email="user3@test.com", password="testpass123"
)
self.client3 = APIClient()
self.client3.force_authenticate(user=self.user3)
# User 1's account group shared with user 2
self.shared_group = AccountGroup.all_objects.create(
name="Shared Group", owner=self.user1
)
self.shared_group.shared_with.add(self.user2)
# User 1's private account group
self.private_group = AccountGroup.all_objects.create(
name="Private Group", owner=self.user1
)
# User 1's public account group
self.public_group = AccountGroup.all_objects.create(
name="Public Group", owner=self.user1, visibility="public"
)
def test_user_can_see_shared_account_groups(self):
"""User2 should see account groups shared with them."""
response = self.client2.get("/api/account-groups/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
group_ids = [g["id"] for g in response.data["results"]]
self.assertIn(self.shared_group.id, group_ids)
self.assertNotIn(self.private_group.id, group_ids)
def test_user_can_access_shared_account_group_detail(self):
"""User2 should be able to access shared account group details."""
response = self.client2.get(f"/api/account-groups/{self.shared_group.id}/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["name"], "Shared Group")
def test_user_can_see_public_account_groups(self):
"""User3 should see public account groups."""
response = self.client3.get("/api/account-groups/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
group_ids = [g["id"] for g in response.data["results"]]
self.assertIn(self.public_group.id, group_ids)
def test_user_without_share_cannot_access_shared_group(self):
"""User3 should NOT be able to access shared account group."""
response = self.client3.get(f"/api/account-groups/{self.shared_group.id}/")
self.assertIn(response.status_code, ACCESS_DENIED_CODES)

View File

@@ -6,8 +6,11 @@ from rest_framework.response import Response
from apps.accounts.models import AccountGroup, Account
from apps.accounts.services import get_account_balance
from apps.api.custom.pagination import CustomPageNumberPagination
from apps.api.serializers import AccountGroupSerializer, AccountSerializer, AccountBalanceSerializer
from apps.api.serializers import (
AccountGroupSerializer,
AccountSerializer,
AccountBalanceSerializer,
)
class AccountGroupViewSet(viewsets.ModelViewSet):
@@ -15,10 +18,16 @@ class AccountGroupViewSet(viewsets.ModelViewSet):
queryset = AccountGroup.objects.all()
serializer_class = AccountGroupSerializer
pagination_class = CustomPageNumberPagination
filterset_fields = {
"name": ["exact", "icontains"],
"owner": ["exact"],
}
search_fields = ["name"]
ordering_fields = "__all__"
ordering = ["id"]
def get_queryset(self):
return AccountGroup.objects.all().order_by("id")
return AccountGroup.objects.all()
@extend_schema_view(
@@ -33,28 +42,38 @@ class AccountViewSet(viewsets.ModelViewSet):
queryset = Account.objects.all()
serializer_class = AccountSerializer
pagination_class = CustomPageNumberPagination
filterset_fields = {
"name": ["exact", "icontains"],
"group": ["exact", "isnull"],
"currency": ["exact"],
"exchange_currency": ["exact", "isnull"],
"is_asset": ["exact"],
"is_archived": ["exact"],
"owner": ["exact"],
}
search_fields = ["name"]
ordering_fields = "__all__"
ordering = ["id"]
def get_queryset(self):
return (
Account.objects.all()
.order_by("id")
.select_related("group", "currency", "exchange_currency")
return Account.objects.all().select_related(
"group", "currency", "exchange_currency"
)
@action(detail=True, methods=["get"], permission_classes=[IsAuthenticated])
def balance(self, request, pk=None):
"""Get current and projected balance for an account."""
account = self.get_object()
current_balance = get_account_balance(account, paid_only=True)
projected_balance = get_account_balance(account, paid_only=False)
serializer = AccountBalanceSerializer({
"current_balance": current_balance,
"projected_balance": projected_balance,
"currency": account.currency,
})
return Response(serializer.data)
serializer = AccountBalanceSerializer(
{
"current_balance": current_balance,
"projected_balance": projected_balance,
"currency": account.currency,
}
)
return Response(serializer.data)

View File

@@ -9,8 +9,28 @@ from apps.currencies.models import ExchangeRate
class CurrencyViewSet(viewsets.ModelViewSet):
queryset = Currency.objects.all()
serializer_class = CurrencySerializer
filterset_fields = {
'name': ['exact', 'icontains'],
'code': ['exact', 'icontains'],
'decimal_places': ['exact', 'gte', 'lte', 'gt', 'lt'],
'prefix': ['exact', 'icontains'],
'suffix': ['exact', 'icontains'],
'exchange_currency': ['exact'],
'is_archived': ['exact'],
}
search_fields = '__all__'
ordering_fields = '__all__'
class ExchangeRateViewSet(viewsets.ModelViewSet):
queryset = ExchangeRate.objects.all()
serializer_class = ExchangeRateSerializer
filterset_fields = {
'from_currency': ['exact'],
'to_currency': ['exact'],
'rate': ['exact', 'gte', 'lte', 'gt', 'lt'],
'date': ['exact', 'gte', 'lte', 'gt', 'lt'],
'automatic': ['exact'],
}
search_fields = '__all__'
ordering_fields = '__all__'

View File

@@ -8,6 +8,19 @@ from apps.api.serializers import DCAStrategySerializer, DCAEntrySerializer
class DCAStrategyViewSet(viewsets.ModelViewSet):
queryset = DCAStrategy.objects.all()
serializer_class = DCAStrategySerializer
filterset_fields = {
"name": ["exact", "icontains"],
"target_currency": ["exact"],
"payment_currency": ["exact"],
"notes": ["exact", "icontains"],
"created_at": ["exact", "gte", "lte", "gt", "lt"],
"updated_at": ["exact", "gte", "lte", "gt", "lt"],
}
search_fields = ["name", "notes"]
ordering_fields = "__all__"
def get_queryset(self):
return DCAStrategy.objects.all()
@action(detail=True, methods=["get"])
def investment_frequency(self, request, pk=None):
@@ -32,10 +45,22 @@ class DCAStrategyViewSet(viewsets.ModelViewSet):
class DCAEntryViewSet(viewsets.ModelViewSet):
queryset = DCAEntry.objects.all()
serializer_class = DCAEntrySerializer
filterset_fields = {
"strategy": ["exact"],
"date": ["exact", "gte", "lte", "gt", "lt"],
"amount_paid": ["exact", "gte", "lte", "gt", "lt"],
"amount_received": ["exact", "gte", "lte", "gt", "lt"],
"expense_transaction": ["exact", "isnull"],
"income_transaction": ["exact", "isnull"],
"notes": ["exact", "icontains"],
"created_at": ["exact", "gte", "lte", "gt", "lt"],
"updated_at": ["exact", "gte", "lte", "gt", "lt"],
}
search_fields = ["notes"]
ordering_fields = "__all__"
ordering = ["-date"]
def get_queryset(self):
queryset = DCAEntry.objects.all()
strategy_id = self.request.query_params.get("strategy", None)
if strategy_id is not None:
queryset = queryset.filter(strategy_id=strategy_id)
return queryset
# Filter entries by strategies the user has access to
accessible_strategies = DCAStrategy.objects.all()
return DCAEntry.objects.filter(strategy__in=accessible_strategies)

View File

@@ -28,6 +28,14 @@ class ImportProfileViewSet(viewsets.ReadOnlyModelViewSet):
queryset = ImportProfile.objects.all()
serializer_class = ImportProfileSerializer
permission_classes = [IsAuthenticated]
filterset_fields = {
'name': ['exact', 'icontains'],
'yaml_config': ['exact', 'icontains'],
'version': ['exact'],
}
search_fields = ['name', 'yaml_config']
ordering_fields = '__all__'
ordering = ['name']
@extend_schema_view(
@@ -55,6 +63,22 @@ class ImportRunViewSet(viewsets.ReadOnlyModelViewSet):
queryset = ImportRun.objects.all().order_by("-id")
serializer_class = ImportRunSerializer
permission_classes = [IsAuthenticated]
filterset_fields = {
'status': ['exact'],
'profile': ['exact'],
'file_name': ['exact', 'icontains'],
'logs': ['exact', 'icontains'],
'processed_rows': ['exact', 'gte', 'lte', 'gt', 'lt'],
'total_rows': ['exact', 'gte', 'lte', 'gt', 'lt'],
'successful_rows': ['exact', 'gte', 'lte', 'gt', 'lt'],
'skipped_rows': ['exact', 'gte', 'lte', 'gt', 'lt'],
'failed_rows': ['exact', 'gte', 'lte', 'gt', 'lt'],
'started_at': ['exact', 'gte', 'lte', 'gt', 'lt', 'isnull'],
'finished_at': ['exact', 'gte', 'lte', 'gt', 'lt', 'isnull'],
}
search_fields = ['file_name', 'logs']
ordering_fields = '__all__'
ordering = ['-id']
def get_queryset(self):
queryset = super().get_queryset()

View File

@@ -2,7 +2,6 @@ from copy import deepcopy
from rest_framework import viewsets
from apps.api.custom.pagination import CustomPageNumberPagination
from apps.api.serializers import (
TransactionSerializer,
TransactionCategorySerializer,
@@ -25,7 +24,34 @@ from apps.rules.signals import transaction_updated, transaction_created
class TransactionViewSet(viewsets.ModelViewSet):
queryset = Transaction.objects.all()
serializer_class = TransactionSerializer
pagination_class = CustomPageNumberPagination
filterset_fields = {
"account": ["exact"],
"type": ["exact"],
"is_paid": ["exact"],
"date": ["exact", "gte", "lte", "gt", "lt"],
"reference_date": ["exact", "gte", "lte", "gt", "lt"],
"mute": ["exact"],
"amount": ["exact", "gte", "lte", "gt", "lt"],
"description": ["exact", "icontains"],
"notes": ["exact", "icontains"],
"category": ["exact", "isnull"],
"installment_plan": ["exact", "isnull"],
"installment_id": ["exact", "gte", "lte"],
"recurring_transaction": ["exact", "isnull"],
"internal_note": ["exact", "icontains"],
"internal_id": ["exact"],
"deleted": ["exact"],
"created_at": ["exact", "gte", "lte", "gt", "lt"],
"updated_at": ["exact", "gte", "lte", "gt", "lt"],
"deleted_at": ["exact", "gte", "lte", "gt", "lt", "isnull"],
"owner": ["exact"],
}
search_fields = ["description", "notes", "internal_note"]
ordering_fields = "__all__"
ordering = ["-id"]
def get_queryset(self):
return Transaction.objects.all()
def perform_create(self, serializer):
instance = serializer.save()
@@ -40,50 +66,109 @@ class TransactionViewSet(viewsets.ModelViewSet):
kwargs["partial"] = True
return self.update(request, *args, **kwargs)
def get_queryset(self):
return Transaction.objects.all().order_by("-id")
class TransactionCategoryViewSet(viewsets.ModelViewSet):
queryset = TransactionCategory.objects.all()
serializer_class = TransactionCategorySerializer
pagination_class = CustomPageNumberPagination
filterset_fields = {
"name": ["exact", "icontains"],
"mute": ["exact"],
"active": ["exact"],
"owner": ["exact"],
}
search_fields = ["name"]
ordering_fields = "__all__"
ordering = ["id"]
def get_queryset(self):
return TransactionCategory.objects.all().order_by("id")
return TransactionCategory.objects.all()
class TransactionTagViewSet(viewsets.ModelViewSet):
queryset = TransactionTag.objects.all()
serializer_class = TransactionTagSerializer
pagination_class = CustomPageNumberPagination
filterset_fields = {
"name": ["exact", "icontains"],
"active": ["exact"],
"owner": ["exact"],
}
search_fields = ["name"]
ordering_fields = "__all__"
ordering = ["id"]
def get_queryset(self):
return TransactionTag.objects.all().order_by("id")
return TransactionTag.objects.all()
class TransactionEntityViewSet(viewsets.ModelViewSet):
queryset = TransactionEntity.objects.all()
serializer_class = TransactionEntitySerializer
pagination_class = CustomPageNumberPagination
filterset_fields = {
"name": ["exact", "icontains"],
"active": ["exact"],
"owner": ["exact"],
}
search_fields = ["name"]
ordering_fields = "__all__"
ordering = ["id"]
def get_queryset(self):
return TransactionEntity.objects.all().order_by("id")
return TransactionEntity.objects.all()
class InstallmentPlanViewSet(viewsets.ModelViewSet):
queryset = InstallmentPlan.objects.all()
serializer_class = InstallmentPlanSerializer
pagination_class = CustomPageNumberPagination
filterset_fields = {
"account": ["exact"],
"type": ["exact"],
"description": ["exact", "icontains"],
"number_of_installments": ["exact", "gte", "lte", "gt", "lt"],
"installment_start": ["exact", "gte", "lte", "gt", "lt"],
"installment_total_number": ["exact", "gte", "lte", "gt", "lt"],
"start_date": ["exact", "gte", "lte", "gt", "lt"],
"reference_date": ["exact", "gte", "lte", "gt", "lt", "isnull"],
"end_date": ["exact", "gte", "lte", "gt", "lt", "isnull"],
"recurrence": ["exact"],
"installment_amount": ["exact", "gte", "lte", "gt", "lt"],
"category": ["exact", "isnull"],
"notes": ["exact", "icontains"],
"add_description_to_transaction": ["exact"],
"add_notes_to_transaction": ["exact"],
}
search_fields = ["description", "notes"]
ordering_fields = "__all__"
ordering = ["-id"]
def get_queryset(self):
return InstallmentPlan.objects.all().order_by("-id")
return InstallmentPlan.objects.all()
class RecurringTransactionViewSet(viewsets.ModelViewSet):
queryset = RecurringTransaction.objects.all()
serializer_class = RecurringTransactionSerializer
pagination_class = CustomPageNumberPagination
filterset_fields = {
"is_paused": ["exact"],
"account": ["exact"],
"type": ["exact"],
"amount": ["exact", "gte", "lte", "gt", "lt"],
"description": ["exact", "icontains"],
"category": ["exact", "isnull"],
"notes": ["exact", "icontains"],
"reference_date": ["exact", "gte", "lte", "gt", "lt", "isnull"],
"start_date": ["exact", "gte", "lte", "gt", "lt"],
"end_date": ["exact", "gte", "lte", "gt", "lt", "isnull"],
"recurrence_type": ["exact"],
"recurrence_interval": ["exact", "gte", "lte", "gt", "lt"],
"keep_at_most": ["exact", "gte", "lte", "gt", "lt"],
"last_generated_date": ["exact", "gte", "lte", "gt", "lt", "isnull"],
"last_generated_reference_date": ["exact", "gte", "lte", "gt", "lt", "isnull"],
"add_description_to_transaction": ["exact"],
"add_notes_to_transaction": ["exact"],
}
search_fields = ["description", "notes"]
ordering_fields = "__all__"
ordering = ["-id"]
def get_queryset(self):
return RecurringTransaction.objects.all().order_by("-id")
return RecurringTransaction.objects.all()

View File

@@ -23,3 +23,6 @@ class CommonConfig(AppConfig):
# Delete the cache for update checks to prevent false-positives when the app is restarted
# this will be recreated by the check_for_updates task
cache.delete("update_check")
# Register system checks for required environment variables
from apps.common import checks # noqa: F401

103
app/apps/common/checks.py Normal file
View File

@@ -0,0 +1,103 @@
"""
Django System Checks for required environment variables.
This module validates that required environment variables (those without defaults)
are present before the application starts.
"""
import os
from django.core.checks import Error, register
# List of environment variables that are required (no default values)
# Based on the README.md documentation
REQUIRED_ENV_VARS = [
("SECRET_KEY", "This is used to provide cryptographic signing."),
("SQL_DATABASE", "The name of your postgres database."),
]
# List of environment variables that must be valid integers if set
INT_ENV_VARS = [
("TASK_WORKERS", "How many workers to have for async tasks."),
("SESSION_EXPIRY_TIME", "The age of session cookies, in seconds."),
("INTERNAL_PORT", "The port on which the app listens on."),
("DJANGO_VITE_DEV_SERVER_PORT", "The port where Vite's dev server is running"),
]
@register()
def check_required_env_vars(app_configs, **kwargs):
"""
Check that all required environment variables are set.
Returns a list of Error objects for any missing required variables.
"""
errors = []
for var_name, description in REQUIRED_ENV_VARS:
value = os.getenv(var_name)
if not value:
errors.append(
Error(
f"Required environment variable '{var_name}' is not set.",
hint=f"{description} Please set this variable in your .env file or environment.",
id="wygiwyh.E001",
)
)
return errors
@register()
def check_int_env_vars(app_configs, **kwargs):
"""
Check that environment variables that should be integers are valid.
Returns a list of Error objects for any invalid integer variables.
"""
errors = []
for var_name, description in INT_ENV_VARS:
value = os.getenv(var_name)
if value is not None:
try:
int(value)
except ValueError:
errors.append(
Error(
f"Environment variable '{var_name}' must be a valid integer, got '{value}'.",
hint=f"{description}",
id="wygiwyh.E002",
)
)
return errors
@register()
def check_soft_delete_config(app_configs, **kwargs):
"""
Check that KEEP_DELETED_TRANSACTIONS_FOR is a valid integer when ENABLE_SOFT_DELETE is enabled.
Returns a list of Error objects if the configuration is invalid.
"""
errors = []
enable_soft_delete = os.getenv("ENABLE_SOFT_DELETE", "false").lower() == "true"
if enable_soft_delete:
keep_deleted_for = os.getenv("KEEP_DELETED_TRANSACTIONS_FOR")
if keep_deleted_for is not None:
try:
int(keep_deleted_for)
except ValueError:
errors.append(
Error(
f"Environment variable 'KEEP_DELETED_TRANSACTIONS_FOR' must be a valid integer when ENABLE_SOFT_DELETE is enabled, got '{keep_deleted_for}'.",
hint="Time in days to keep soft deleted transactions for. Set to 0 to keep all transactions indefinitely.",
id="wygiwyh.E003",
)
)
return errors

View File

@@ -1,5 +1,4 @@
import logging
from datetime import timedelta
from django.db.models import QuerySet
from django.utils import timezone
@@ -258,7 +257,10 @@ class ExchangeRateFetcher:
processed_pairs.add((from_currency.id, to_currency.id))
service.last_fetch = timezone.now()
service.failure_count = 0
service.save()
except Exception as e:
logger.error(f"Error fetching rates for {service.name}: {e}")
service.failure_count += 1
service.save()

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.10 on 2026-01-10 06:08
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('currencies', '0022_currency_is_archived'),
]
operations = [
migrations.AddField(
model_name='exchangerateservice',
name='failure_count',
field=models.PositiveIntegerField(default=0),
),
]

View File

@@ -136,6 +136,8 @@ class ExchangeRateService(models.Model):
null=True, blank=True, verbose_name=_("Last Successful Fetch")
)
failure_count = models.PositiveIntegerField(default=0)
target_currencies = models.ManyToManyField(
Currency,
verbose_name=_("Target Currencies"),
@@ -237,7 +239,7 @@ class ExchangeRateService(models.Model):
hours = self._parse_hour_ranges(self.fetch_interval)
# Store in normalized format (optional)
self.fetch_interval = ",".join(str(h) for h in sorted(hours))
except ValueError as e:
except ValueError:
raise ValidationError(
{
"fetch_interval": _(
@@ -248,7 +250,7 @@ class ExchangeRateService(models.Model):
)
except ValidationError:
raise
except Exception as e:
except Exception:
raise ValidationError(
{
"fetch_interval": _(

View File

@@ -0,0 +1 @@
# Tests package for currencies app

View File

@@ -0,0 +1,109 @@
from decimal import Decimal
from unittest.mock import patch, MagicMock
from django.test import TestCase
from django.utils import timezone
from apps.currencies.models import Currency, ExchangeRateService
from apps.currencies.exchange_rates.fetcher import ExchangeRateFetcher
class ExchangeRateServiceFailureTrackingTests(TestCase):
"""Tests for the failure count tracking functionality."""
def setUp(self):
"""Set up test data."""
self.usd = Currency.objects.create(
code="USD", name="US Dollar", decimal_places=2, prefix="$ "
)
self.eur = Currency.objects.create(
code="EUR", name="Euro", decimal_places=2, prefix=""
)
self.eur.exchange_currency = self.usd
self.eur.save()
self.service = ExchangeRateService.objects.create(
name="Test Service",
service_type=ExchangeRateService.ServiceType.FRANKFURTER,
is_active=True,
)
self.service.target_currencies.add(self.eur)
def test_failure_count_increments_on_provider_error(self):
"""Test that failure_count increments when provider raises an exception."""
self.assertEqual(self.service.failure_count, 0)
with patch.object(
self.service, "get_provider", side_effect=Exception("API Error")
):
ExchangeRateFetcher._fetch_service_rates(self.service)
self.service.refresh_from_db()
self.assertEqual(self.service.failure_count, 1)
def test_failure_count_resets_on_success(self):
"""Test that failure_count resets to 0 on successful fetch."""
# Set initial failure count
self.service.failure_count = 5
self.service.save()
# Mock a successful provider
mock_provider = MagicMock()
mock_provider.requires_api_key.return_value = False
mock_provider.get_rates.return_value = [(self.usd, self.eur, Decimal("0.85"))]
mock_provider.rates_inverted = False
with patch.object(self.service, "get_provider", return_value=mock_provider):
ExchangeRateFetcher._fetch_service_rates(self.service)
self.service.refresh_from_db()
self.assertEqual(self.service.failure_count, 0)
def test_failure_count_accumulates_across_fetches(self):
"""Test that failure_count accumulates with consecutive failures."""
self.assertEqual(self.service.failure_count, 0)
with patch.object(
self.service, "get_provider", side_effect=Exception("API Error")
):
ExchangeRateFetcher._fetch_service_rates(self.service)
self.service.refresh_from_db()
self.assertEqual(self.service.failure_count, 1)
ExchangeRateFetcher._fetch_service_rates(self.service)
self.service.refresh_from_db()
self.assertEqual(self.service.failure_count, 2)
ExchangeRateFetcher._fetch_service_rates(self.service)
self.service.refresh_from_db()
self.assertEqual(self.service.failure_count, 3)
def test_last_fetch_not_updated_on_failure(self):
"""Test that last_fetch is NOT updated when a failure occurs."""
original_last_fetch = self.service.last_fetch
self.assertIsNone(original_last_fetch)
with patch.object(
self.service, "get_provider", side_effect=Exception("API Error")
):
ExchangeRateFetcher._fetch_service_rates(self.service)
self.service.refresh_from_db()
self.assertIsNone(self.service.last_fetch)
self.assertEqual(self.service.failure_count, 1)
def test_last_fetch_updated_on_success(self):
"""Test that last_fetch IS updated when fetch succeeds."""
self.assertIsNone(self.service.last_fetch)
mock_provider = MagicMock()
mock_provider.requires_api_key.return_value = False
mock_provider.get_rates.return_value = [(self.usd, self.eur, Decimal("0.85"))]
mock_provider.rates_inverted = False
with patch.object(self.service, "get_provider", return_value=mock_provider):
ExchangeRateFetcher._fetch_service_rates(self.service)
self.service.refresh_from_db()
self.assertIsNotNone(self.service.last_fetch)
self.assertEqual(self.service.failure_count, 0)

View File

@@ -23,7 +23,7 @@ def currencies_index(request):
@login_required
@require_http_methods(["GET"])
def currencies_list(request):
currencies = Currency.objects.all().order_by("id")
currencies = Currency.objects.all().order_by("name")
return render(
request,
"currencies/fragments/list.html",

View File

@@ -1,4 +1,3 @@
# apps/dca_tracker/views.py
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.db.models import Sum, Avg
@@ -23,7 +22,7 @@ def strategy_index(request):
@only_htmx
@login_required
def strategy_list(request):
strategies = DCAStrategy.objects.all().order_by("created_at")
strategies = DCAStrategy.objects.all().order_by("name")
return render(
request, "dca/fragments/strategy/list.html", {"strategies": strategies}
)
@@ -234,7 +233,7 @@ def strategy_entry_add(request, strategy_id):
if request.method == "POST":
form = DCAEntryForm(request.POST, strategy=strategy)
if form.is_valid():
entry = form.save()
form.save()
messages.success(request, _("Entry added successfully"))
return HttpResponse(

View File

@@ -1,8 +1,10 @@
from import_export import fields, resources
from import_export.widgets import ForeignKeyWidget
from apps.accounts.models import Account
from apps.export_app.widgets.foreign_key import AutoCreateForeignKeyWidget
from apps.export_app.widgets.foreign_key import (
AllObjectsForeignKeyWidget,
AutoCreateForeignKeyWidget,
)
from apps.export_app.widgets.many_to_many import AutoCreateManyToManyWidget
from apps.export_app.widgets.string import EmptyStringToNoneField
from apps.transactions.models import (
@@ -20,7 +22,7 @@ class TransactionResource(resources.ModelResource):
account = fields.Field(
attribute="account",
column_name="account",
widget=ForeignKeyWidget(Account, "name"),
widget=AllObjectsForeignKeyWidget(Account, "name"),
)
category = fields.Field(
@@ -86,7 +88,7 @@ class RecurringTransactionResource(resources.ModelResource):
account = fields.Field(
attribute="account",
column_name="account",
widget=ForeignKeyWidget(Account, "name"),
widget=AllObjectsForeignKeyWidget(Account, "name"),
)
category = fields.Field(
@@ -119,12 +121,16 @@ class RecurringTransactionResource(resources.ModelResource):
def get_queryset(self):
return RecurringTransaction.all_objects.all()
def dehydrate_account_owner(self, obj):
"""Export the account's owner ID for proper import matching."""
return obj.account.owner_id if obj.account else None
class InstallmentPlanResource(resources.ModelResource):
account = fields.Field(
attribute="account",
column_name="account",
widget=ForeignKeyWidget(Account, "name"),
widget=AllObjectsForeignKeyWidget(Account, "name"),
)
category = fields.Field(
@@ -156,3 +162,7 @@ class InstallmentPlanResource(resources.ModelResource):
def get_queryset(self):
return InstallmentPlan.all_objects.all()
def dehydrate_account_owner(self, obj):
"""Export the account's owner ID for proper import matching."""
return obj.account.owner_id if obj.account else None

View File

@@ -1,6 +1,60 @@
from import_export.widgets import ForeignKeyWidget
class AllObjectsForeignKeyWidget(ForeignKeyWidget):
"""
ForeignKeyWidget that uses 'all_objects' manager for lookups,
bypassing user-filtered managers like SharedObjectManager.
Also filters by owner if available in the row data.
"""
def get_queryset(self, value, row, *args, **kwargs):
# Use all_objects manager if available, otherwise fall back to default
if hasattr(self.model, "all_objects"):
qs = self.model.all_objects.all()
# Filter by owner if the row has an owner field and the model has owner
if row:
# Check for direct owner field first
owner_id = row.get("owner") if "owner" in row else None
# Fall back to account_owner for models like InstallmentPlan
if not owner_id and "account_owner" in row:
owner_id = row.get("account_owner")
# If still no owner, try to get it from the existing record's account
# This handles backward compatibility with older exports
if not owner_id and "id" in row and row.get("id"):
try:
# Try to find the existing record and get owner from its account
from apps.transactions.models import (
InstallmentPlan,
RecurringTransaction,
)
record_id = row.get("id")
# Try to find the existing InstallmentPlan or RecurringTransaction
for model_class in [InstallmentPlan, RecurringTransaction]:
try:
existing = model_class.all_objects.get(id=record_id)
if existing.account:
owner_id = existing.account.owner_id
break
except model_class.DoesNotExist:
continue
except Exception:
pass
# Final fallback: use the current logged-in user
# This handles restoring to a fresh database with older exports
if not owner_id:
from apps.common.middleware.thread_local import get_current_user
user = get_current_user()
if user and user.is_authenticated:
owner_id = user.id
if owner_id:
qs = qs.filter(owner_id=owner_id)
return qs
return super().get_queryset(value, row, *args, **kwargs)
class AutoCreateForeignKeyWidget(ForeignKeyWidget):
def clean(self, value, row=None, *args, **kwargs):
if value:

View File

@@ -8,7 +8,6 @@ is only used for string fields (not dates, decimals, etc.).
from datetime import date
from decimal import Decimal
from unittest.mock import MagicMock, patch
from django.test import TestCase

View File

@@ -49,4 +49,14 @@ urlpatterns = [
views.emergency_fund,
name="insights_emergency_fund",
),
path(
"insights/year-by-year/",
views.year_by_year,
name="insights_year_by_year",
),
path(
"insights/month-by-month/",
views.month_by_month,
name="insights_month_by_month",
),
]

View File

@@ -0,0 +1,316 @@
from collections import OrderedDict
from decimal import Decimal
from django.db import models
from django.db.models import Sum, Case, When, Value
from django.db.models.functions import Coalesce
from django.utils import timezone
from apps.currencies.models import Currency
from apps.currencies.utils.convert import convert
from apps.transactions.models import Transaction
def get_month_by_month_data(year=None, group_by="categories"):
"""
Aggregate transaction totals by month for a specific year, grouped by categories, tags, or entities.
Args:
year: The year to filter transactions (defaults to current year)
group_by: One of "categories", "tags", or "entities"
Returns:
{
"year": 2025,
"available_years": [2025, 2024, ...],
"months": [1, 2, 3, ..., 12],
"items": {
item_id: {
"name": "Item Name",
"month_totals": {
1: {"currencies": {...}},
...
},
"total": {"currencies": {...}}
},
...
},
"month_totals": {...},
"grand_total": {"currencies": {...}}
}
"""
if year is None:
year = timezone.localdate(timezone.now()).year
# Base queryset - all paid transactions, non-muted
transactions = Transaction.objects.filter(
is_paid=True,
account__is_archived=False,
).exclude(account__currency__is_archived=True)
# Get available years for the selector
available_years = list(
transactions.values_list("reference_date__year", flat=True)
.distinct()
.order_by("-reference_date__year")
)
# Filter by the selected year
transactions = transactions.filter(reference_date__year=year)
# Define grouping fields based on group_by parameter
if group_by == "tags":
group_field = "tags"
name_field = "tags__name"
elif group_by == "entities":
group_field = "entities"
name_field = "entities__name"
else: # Default to categories
group_field = "category"
name_field = "category__name"
# Months 1-12
months = list(range(1, 13))
if not available_years:
return {
"year": year,
"available_years": [],
"months": months,
"items": {},
"month_totals": {},
"grand_total": {"currencies": {}},
}
# Aggregate by group, month, and currency
metrics = (
transactions.values(
group_field,
name_field,
"reference_date__month",
"account__currency",
"account__currency__code",
"account__currency__name",
"account__currency__decimal_places",
"account__currency__prefix",
"account__currency__suffix",
"account__currency__exchange_currency",
)
.annotate(
expense_total=Coalesce(
Sum(
Case(
When(type=Transaction.Type.EXPENSE, then="amount"),
default=Value(0),
output_field=models.DecimalField(),
)
),
Decimal("0"),
),
income_total=Coalesce(
Sum(
Case(
When(type=Transaction.Type.INCOME, then="amount"),
default=Value(0),
output_field=models.DecimalField(),
)
),
Decimal("0"),
),
)
.order_by(name_field, "reference_date__month")
)
# Build result structure
result = {
"year": year,
"available_years": available_years,
"months": months,
"items": OrderedDict(),
"month_totals": {},
"grand_total": {"currencies": {}},
}
# Store currency info for later use in totals
currency_info = {}
for metric in metrics:
item_id = metric[group_field]
item_name = metric[name_field]
month = metric["reference_date__month"]
currency_id = metric["account__currency"]
# Use a consistent key for None (uncategorized/untagged/no entity)
item_key = item_id if item_id is not None else "__none__"
if item_key not in result["items"]:
result["items"][item_key] = {
"name": item_name,
"month_totals": {},
"total": {"currencies": {}},
}
if month not in result["items"][item_key]["month_totals"]:
result["items"][item_key]["month_totals"][month] = {"currencies": {}}
# Calculate final total (income - expense)
final_total = metric["income_total"] - metric["expense_total"]
# Store currency info for totals calculation
if currency_id not in currency_info:
currency_info[currency_id] = {
"code": metric["account__currency__code"],
"name": metric["account__currency__name"],
"decimal_places": metric["account__currency__decimal_places"],
"prefix": metric["account__currency__prefix"],
"suffix": metric["account__currency__suffix"],
"exchange_currency_id": metric["account__currency__exchange_currency"],
}
currency_data = {
"currency": {
"code": metric["account__currency__code"],
"name": metric["account__currency__name"],
"decimal_places": metric["account__currency__decimal_places"],
"prefix": metric["account__currency__prefix"],
"suffix": metric["account__currency__suffix"],
},
"final_total": final_total,
"income_total": metric["income_total"],
"expense_total": metric["expense_total"],
}
# Handle currency conversion if exchange currency is set
if metric["account__currency__exchange_currency"]:
from_currency = Currency.objects.get(id=currency_id)
exchange_currency = Currency.objects.get(
id=metric["account__currency__exchange_currency"]
)
converted_amount, prefix, suffix, decimal_places = convert(
amount=final_total,
from_currency=from_currency,
to_currency=exchange_currency,
)
if converted_amount is not None:
currency_data["exchanged"] = {
"final_total": converted_amount,
"currency": {
"prefix": prefix,
"suffix": suffix,
"decimal_places": decimal_places,
"code": exchange_currency.code,
"name": exchange_currency.name,
},
}
result["items"][item_key]["month_totals"][month]["currencies"][currency_id] = (
currency_data
)
# Accumulate item total (across all months for this item)
if currency_id not in result["items"][item_key]["total"]["currencies"]:
result["items"][item_key]["total"]["currencies"][currency_id] = {
"currency": currency_data["currency"].copy(),
"final_total": Decimal("0"),
}
result["items"][item_key]["total"]["currencies"][currency_id][
"final_total"
] += final_total
# Accumulate month total (across all items for this month)
if month not in result["month_totals"]:
result["month_totals"][month] = {"currencies": {}}
if currency_id not in result["month_totals"][month]["currencies"]:
result["month_totals"][month]["currencies"][currency_id] = {
"currency": currency_data["currency"].copy(),
"final_total": Decimal("0"),
}
result["month_totals"][month]["currencies"][currency_id]["final_total"] += (
final_total
)
# Accumulate grand total
if currency_id not in result["grand_total"]["currencies"]:
result["grand_total"]["currencies"][currency_id] = {
"currency": currency_data["currency"].copy(),
"final_total": Decimal("0"),
}
result["grand_total"]["currencies"][currency_id]["final_total"] += final_total
# Add currency conversion for item totals
for item_key, item_data in result["items"].items():
for currency_id, total_data in item_data["total"]["currencies"].items():
if currency_info[currency_id]["exchange_currency_id"]:
from_currency = Currency.objects.get(id=currency_id)
exchange_currency = Currency.objects.get(
id=currency_info[currency_id]["exchange_currency_id"]
)
converted_amount, prefix, suffix, decimal_places = convert(
amount=total_data["final_total"],
from_currency=from_currency,
to_currency=exchange_currency,
)
if converted_amount is not None:
total_data["exchanged"] = {
"final_total": converted_amount,
"currency": {
"prefix": prefix,
"suffix": suffix,
"decimal_places": decimal_places,
"code": exchange_currency.code,
"name": exchange_currency.name,
},
}
# Add currency conversion for month totals
for month, month_data in result["month_totals"].items():
for currency_id, total_data in month_data["currencies"].items():
if currency_info[currency_id]["exchange_currency_id"]:
from_currency = Currency.objects.get(id=currency_id)
exchange_currency = Currency.objects.get(
id=currency_info[currency_id]["exchange_currency_id"]
)
converted_amount, prefix, suffix, decimal_places = convert(
amount=total_data["final_total"],
from_currency=from_currency,
to_currency=exchange_currency,
)
if converted_amount is not None:
total_data["exchanged"] = {
"final_total": converted_amount,
"currency": {
"prefix": prefix,
"suffix": suffix,
"decimal_places": decimal_places,
"code": exchange_currency.code,
"name": exchange_currency.name,
},
}
# Add currency conversion for grand total
for currency_id, total_data in result["grand_total"]["currencies"].items():
if currency_info[currency_id]["exchange_currency_id"]:
from_currency = Currency.objects.get(id=currency_id)
exchange_currency = Currency.objects.get(
id=currency_info[currency_id]["exchange_currency_id"]
)
converted_amount, prefix, suffix, decimal_places = convert(
amount=total_data["final_total"],
from_currency=from_currency,
to_currency=exchange_currency,
)
if converted_amount is not None:
total_data["exchanged"] = {
"final_total": converted_amount,
"currency": {
"prefix": prefix,
"suffix": suffix,
"decimal_places": decimal_places,
"code": exchange_currency.code,
"name": exchange_currency.name,
},
}
return result

View File

@@ -0,0 +1,303 @@
from collections import OrderedDict
from decimal import Decimal
from django.db import models
from django.db.models import Sum, Case, When, Value
from django.db.models.functions import Coalesce
from apps.currencies.models import Currency
from apps.currencies.utils.convert import convert
from apps.transactions.models import Transaction
def get_year_by_year_data(group_by="categories"):
"""
Aggregate transaction totals by year for categories, tags, or entities.
Args:
group_by: One of "categories", "tags", or "entities"
Returns:
{
"years": [2025, 2024, ...], # Sorted descending
"items": {
item_id: {
"name": "Item Name",
"year_totals": {
2025: {"currencies": {...}},
...
},
"total": {"currencies": {...}} # Sum across all years
},
...
},
"year_totals": { # Sum across all items for each year
2025: {"currencies": {...}},
...
},
"grand_total": {"currencies": {...}} # Sum of everything
}
"""
# Base queryset - all paid transactions, non-muted
transactions = Transaction.objects.filter(
is_paid=True,
account__is_archived=False,
).exclude(account__currency__is_archived=True)
# Define grouping fields based on group_by parameter
if group_by == "tags":
group_field = "tags"
name_field = "tags__name"
elif group_by == "entities":
group_field = "entities"
name_field = "entities__name"
else: # Default to categories
group_field = "category"
name_field = "category__name"
# Get all unique years with transactions
years = (
transactions.values_list("reference_date__year", flat=True)
.distinct()
.order_by("-reference_date__year")
)
years = list(years)
if not years:
return {
"years": [],
"items": {},
"year_totals": {},
"grand_total": {"currencies": {}},
}
# Aggregate by group, year, and currency
metrics = (
transactions.values(
group_field,
name_field,
"reference_date__year",
"account__currency",
"account__currency__code",
"account__currency__name",
"account__currency__decimal_places",
"account__currency__prefix",
"account__currency__suffix",
"account__currency__exchange_currency",
)
.annotate(
expense_total=Coalesce(
Sum(
Case(
When(type=Transaction.Type.EXPENSE, then="amount"),
default=Value(0),
output_field=models.DecimalField(),
)
),
Decimal("0"),
),
income_total=Coalesce(
Sum(
Case(
When(type=Transaction.Type.INCOME, then="amount"),
default=Value(0),
output_field=models.DecimalField(),
)
),
Decimal("0"),
),
)
.order_by(name_field, "-reference_date__year")
)
# Build result structure
result = {
"years": years,
"items": OrderedDict(),
"year_totals": {}, # Totals per year across all items
"grand_total": {"currencies": {}}, # Grand total across everything
}
# Store currency info for later use in totals
currency_info = {}
for metric in metrics:
item_id = metric[group_field]
item_name = metric[name_field]
year = metric["reference_date__year"]
currency_id = metric["account__currency"]
# Use a consistent key for None (uncategorized/untagged/no entity)
item_key = item_id if item_id is not None else "__none__"
if item_key not in result["items"]:
result["items"][item_key] = {
"name": item_name,
"year_totals": {},
"total": {"currencies": {}}, # Total for this item across all years
}
if year not in result["items"][item_key]["year_totals"]:
result["items"][item_key]["year_totals"][year] = {"currencies": {}}
# Calculate final total (income - expense)
final_total = metric["income_total"] - metric["expense_total"]
# Store currency info for totals calculation
if currency_id not in currency_info:
currency_info[currency_id] = {
"code": metric["account__currency__code"],
"name": metric["account__currency__name"],
"decimal_places": metric["account__currency__decimal_places"],
"prefix": metric["account__currency__prefix"],
"suffix": metric["account__currency__suffix"],
"exchange_currency_id": metric["account__currency__exchange_currency"],
}
currency_data = {
"currency": {
"code": metric["account__currency__code"],
"name": metric["account__currency__name"],
"decimal_places": metric["account__currency__decimal_places"],
"prefix": metric["account__currency__prefix"],
"suffix": metric["account__currency__suffix"],
},
"final_total": final_total,
"income_total": metric["income_total"],
"expense_total": metric["expense_total"],
}
# Handle currency conversion if exchange currency is set
if metric["account__currency__exchange_currency"]:
from_currency = Currency.objects.get(id=currency_id)
exchange_currency = Currency.objects.get(
id=metric["account__currency__exchange_currency"]
)
converted_amount, prefix, suffix, decimal_places = convert(
amount=final_total,
from_currency=from_currency,
to_currency=exchange_currency,
)
if converted_amount is not None:
currency_data["exchanged"] = {
"final_total": converted_amount,
"currency": {
"prefix": prefix,
"suffix": suffix,
"decimal_places": decimal_places,
"code": exchange_currency.code,
"name": exchange_currency.name,
},
}
result["items"][item_key]["year_totals"][year]["currencies"][currency_id] = (
currency_data
)
# Accumulate item total (across all years for this item)
if currency_id not in result["items"][item_key]["total"]["currencies"]:
result["items"][item_key]["total"]["currencies"][currency_id] = {
"currency": currency_data["currency"].copy(),
"final_total": Decimal("0"),
}
result["items"][item_key]["total"]["currencies"][currency_id][
"final_total"
] += final_total
# Accumulate year total (across all items for this year)
if year not in result["year_totals"]:
result["year_totals"][year] = {"currencies": {}}
if currency_id not in result["year_totals"][year]["currencies"]:
result["year_totals"][year]["currencies"][currency_id] = {
"currency": currency_data["currency"].copy(),
"final_total": Decimal("0"),
}
result["year_totals"][year]["currencies"][currency_id]["final_total"] += (
final_total
)
# Accumulate grand total
if currency_id not in result["grand_total"]["currencies"]:
result["grand_total"]["currencies"][currency_id] = {
"currency": currency_data["currency"].copy(),
"final_total": Decimal("0"),
}
result["grand_total"]["currencies"][currency_id]["final_total"] += final_total
# Add currency conversion for item totals
for item_key, item_data in result["items"].items():
for currency_id, total_data in item_data["total"]["currencies"].items():
if currency_info[currency_id]["exchange_currency_id"]:
from_currency = Currency.objects.get(id=currency_id)
exchange_currency = Currency.objects.get(
id=currency_info[currency_id]["exchange_currency_id"]
)
converted_amount, prefix, suffix, decimal_places = convert(
amount=total_data["final_total"],
from_currency=from_currency,
to_currency=exchange_currency,
)
if converted_amount is not None:
total_data["exchanged"] = {
"final_total": converted_amount,
"currency": {
"prefix": prefix,
"suffix": suffix,
"decimal_places": decimal_places,
"code": exchange_currency.code,
"name": exchange_currency.name,
},
}
# Add currency conversion for year totals
for year, year_data in result["year_totals"].items():
for currency_id, total_data in year_data["currencies"].items():
if currency_info[currency_id]["exchange_currency_id"]:
from_currency = Currency.objects.get(id=currency_id)
exchange_currency = Currency.objects.get(
id=currency_info[currency_id]["exchange_currency_id"]
)
converted_amount, prefix, suffix, decimal_places = convert(
amount=total_data["final_total"],
from_currency=from_currency,
to_currency=exchange_currency,
)
if converted_amount is not None:
total_data["exchanged"] = {
"final_total": converted_amount,
"currency": {
"prefix": prefix,
"suffix": suffix,
"decimal_places": decimal_places,
"code": exchange_currency.code,
"name": exchange_currency.name,
},
}
# Add currency conversion for grand total
for currency_id, total_data in result["grand_total"]["currencies"].items():
if currency_info[currency_id]["exchange_currency_id"]:
from_currency = Currency.objects.get(id=currency_id)
exchange_currency = Currency.objects.get(
id=currency_info[currency_id]["exchange_currency_id"]
)
converted_amount, prefix, suffix, decimal_places = convert(
amount=total_data["final_total"],
from_currency=from_currency,
to_currency=exchange_currency,
)
if converted_amount is not None:
total_data["exchanged"] = {
"final_total": converted_amount,
"currency": {
"prefix": prefix,
"suffix": suffix,
"decimal_places": decimal_places,
"code": exchange_currency.code,
"name": exchange_currency.name,
},
}
return result

View File

@@ -26,6 +26,8 @@ from apps.insights.utils.sankey import (
generate_sankey_data_by_currency,
)
from apps.insights.utils.transactions import get_transactions
from apps.insights.utils.year_by_year import get_year_by_year_data
from apps.insights.utils.month_by_month import get_month_by_month_data
from apps.transactions.models import TransactionCategory, Transaction
from apps.transactions.utils.calculations import calculate_currency_totals
@@ -306,3 +308,71 @@ def emergency_fund(request):
"insights/fragments/emergency_fund.html",
{"data": currency_net_worth},
)
@only_htmx
@login_required
@require_http_methods(["GET"])
def year_by_year(request):
if "group_by" in request.GET:
group_by = request.GET["group_by"]
request.session["insights_year_by_year_group_by"] = group_by
else:
group_by = request.session.get("insights_year_by_year_group_by", "categories")
# Validate group_by value
if group_by not in ("categories", "tags", "entities"):
group_by = "categories"
data = get_year_by_year_data(group_by=group_by)
return render(
request,
"insights/fragments/year_by_year.html",
{
"data": data,
"group_by": group_by,
},
)
@only_htmx
@login_required
@require_http_methods(["GET"])
def month_by_month(request):
# Handle year selection
if "year" in request.GET:
try:
year = int(request.GET["year"])
request.session["insights_month_by_month_year"] = year
except (ValueError, TypeError):
year = request.session.get(
"insights_month_by_month_year", timezone.localdate(timezone.now()).year
)
else:
year = request.session.get(
"insights_month_by_month_year", timezone.localdate(timezone.now()).year
)
# Handle group_by selection
if "group_by" in request.GET:
group_by = request.GET["group_by"]
request.session["insights_month_by_month_group_by"] = group_by
else:
group_by = request.session.get("insights_month_by_month_group_by", "categories")
# Validate group_by value
if group_by not in ("categories", "tags", "entities"):
group_by = "categories"
data = get_month_by_month_data(year=year, group_by=group_by)
return render(
request,
"insights/fragments/month_by_month.html",
{
"data": data,
"group_by": group_by,
"selected_year": year,
},
)

View File

@@ -0,0 +1,331 @@
from datetime import date
from decimal import Decimal
from django.contrib.auth import get_user_model
from django.test import TestCase, override_settings
from apps.accounts.models import Account, AccountGroup
from apps.currencies.models import Currency
from apps.transactions.models import (
Transaction,
TransactionCategory,
TransactionTag,
)
@override_settings(
STORAGES={
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"
},
},
WHITENOISE_AUTOREFRESH=True,
)
class MonthlySummaryFilterBehaviorTests(TestCase):
"""Tests for monthly summary views filter behavior.
These tests verify that:
1. Views work correctly without any filters
2. Views work correctly with filters applied
3. The filter detection logic properly uses different querysets
4. Calculated values reflect the applied filters
"""
def setUp(self):
"""Set up test data"""
User = get_user_model()
self.user = User.objects.create_user(
email="testuser@test.com", password="testpass123"
)
self.client.login(username="testuser@test.com", password="testpass123")
self.currency = Currency.objects.create(
code="USD", name="US Dollar", decimal_places=2, prefix="$ "
)
self.account_group = AccountGroup.objects.create(name="Test Group")
self.account = Account.objects.create(
name="Test Account",
group=self.account_group,
currency=self.currency,
is_asset=False,
)
self.category = TransactionCategory.objects.create(
name="Test Category", owner=self.user
)
self.tag = TransactionTag.objects.create(name="TestTag", owner=self.user)
# Create test transactions for December 2025
# Income: 1000 (paid)
self.income_transaction = Transaction.objects.create(
account=self.account,
type=Transaction.Type.INCOME,
is_paid=True,
date=date(2025, 12, 10),
reference_date=date(2025, 12, 1),
amount=Decimal("1000.00"),
description="December Income",
owner=self.user,
)
# Expense: 200 (paid)
self.expense_transaction = Transaction.objects.create(
account=self.account,
type=Transaction.Type.EXPENSE,
is_paid=True,
date=date(2025, 12, 15),
reference_date=date(2025, 12, 1),
amount=Decimal("200.00"),
description="December Expense",
category=self.category,
owner=self.user,
)
self.expense_transaction.tags.add(self.tag)
# Expense: 150 (projected/unpaid)
self.projected_expense = Transaction.objects.create(
account=self.account,
type=Transaction.Type.EXPENSE,
is_paid=False,
date=date(2025, 12, 20),
reference_date=date(2025, 12, 1),
amount=Decimal("150.00"),
description="Projected Expense",
owner=self.user,
)
def _get_currency_data(self, context_dict):
"""Helper to extract data for our test currency from context dict.
The context dict is keyed by currency ID, so we need to find
the entry for our currency.
"""
if not context_dict:
return None
for currency_id, data in context_dict.items():
if data.get("currency", {}).get("code") == "USD":
return data
return None
# --- monthly_summary view tests ---
def test_monthly_summary_no_filter_returns_200(self):
"""Test that monthly_summary returns 200 without filters"""
response = self.client.get(
"/monthly/12/2025/summary/",
HTTP_HX_REQUEST="true",
)
self.assertEqual(response.status_code, 200)
def test_monthly_summary_no_filter_includes_all_transactions(self):
"""Without filters, summary should include all transactions"""
response = self.client.get(
"/monthly/12/2025/summary/",
HTTP_HX_REQUEST="true",
)
context = response.context
# income_current should have the income: 1000
income_current = context.get("income_current", {})
usd_data = self._get_currency_data(income_current)
self.assertIsNotNone(usd_data)
self.assertEqual(usd_data["income_current"], Decimal("1000.00"))
# expense_current should have paid expense: 200
expense_current = context.get("expense_current", {})
usd_data = self._get_currency_data(expense_current)
self.assertIsNotNone(usd_data)
self.assertEqual(usd_data["expense_current"], Decimal("200.00"))
# expense_projected should have unpaid expense: 150
expense_projected = context.get("expense_projected", {})
usd_data = self._get_currency_data(expense_projected)
self.assertIsNotNone(usd_data)
self.assertEqual(usd_data["expense_projected"], Decimal("150.00"))
def test_monthly_summary_type_filter_only_income(self):
"""With type=IN filter, summary should only include income"""
response = self.client.get(
"/monthly/12/2025/summary/?type=IN",
HTTP_HX_REQUEST="true",
)
context = response.context
# income_current should still have 1000
income_current = context.get("income_current", {})
usd_data = self._get_currency_data(income_current)
self.assertIsNotNone(usd_data)
self.assertEqual(usd_data["income_current"], Decimal("1000.00"))
# expense_current should be empty/zero (filtered out)
expense_current = context.get("expense_current", {})
usd_data = self._get_currency_data(expense_current)
if usd_data:
self.assertEqual(usd_data.get("expense_current", 0), Decimal("0"))
# expense_projected should be empty/zero (filtered out)
expense_projected = context.get("expense_projected", {})
usd_data = self._get_currency_data(expense_projected)
if usd_data:
self.assertEqual(usd_data.get("expense_projected", 0), Decimal("0"))
def test_monthly_summary_type_filter_only_expenses(self):
"""With type=EX filter, summary should only include expenses"""
response = self.client.get(
"/monthly/12/2025/summary/?type=EX",
HTTP_HX_REQUEST="true",
)
context = response.context
# income_current should be empty/zero (filtered out)
income_current = context.get("income_current", {})
usd_data = self._get_currency_data(income_current)
if usd_data:
self.assertEqual(usd_data.get("income_current", 0), Decimal("0"))
# expense_current should have 200
expense_current = context.get("expense_current", {})
usd_data = self._get_currency_data(expense_current)
self.assertIsNotNone(usd_data)
self.assertEqual(usd_data["expense_current"], Decimal("200.00"))
# expense_projected should have 150
expense_projected = context.get("expense_projected", {})
usd_data = self._get_currency_data(expense_projected)
self.assertIsNotNone(usd_data)
self.assertEqual(usd_data["expense_projected"], Decimal("150.00"))
def test_monthly_summary_is_paid_filter_only_paid(self):
"""With is_paid=1 filter, summary should only include paid transactions"""
response = self.client.get(
"/monthly/12/2025/summary/?is_paid=1",
HTTP_HX_REQUEST="true",
)
context = response.context
# income_current should have 1000 (paid)
income_current = context.get("income_current", {})
usd_data = self._get_currency_data(income_current)
self.assertIsNotNone(usd_data)
self.assertEqual(usd_data["income_current"], Decimal("1000.00"))
# expense_current should have 200 (paid)
expense_current = context.get("expense_current", {})
usd_data = self._get_currency_data(expense_current)
self.assertIsNotNone(usd_data)
self.assertEqual(usd_data["expense_current"], Decimal("200.00"))
# expense_projected should be empty/zero (filtered out - unpaid)
expense_projected = context.get("expense_projected", {})
usd_data = self._get_currency_data(expense_projected)
if usd_data:
self.assertEqual(usd_data.get("expense_projected", 0), Decimal("0"))
def test_monthly_summary_is_paid_filter_only_unpaid(self):
"""With is_paid=0 filter, summary should only include unpaid transactions"""
response = self.client.get(
"/monthly/12/2025/summary/?is_paid=0",
HTTP_HX_REQUEST="true",
)
context = response.context
# income_current should be empty/zero (filtered out - paid)
income_current = context.get("income_current", {})
usd_data = self._get_currency_data(income_current)
if usd_data:
self.assertEqual(usd_data.get("income_current", 0), Decimal("0"))
# expense_current should be empty/zero (filtered out - paid)
expense_current = context.get("expense_current", {})
usd_data = self._get_currency_data(expense_current)
if usd_data:
self.assertEqual(usd_data.get("expense_current", 0), Decimal("0"))
# expense_projected should have 150 (unpaid)
expense_projected = context.get("expense_projected", {})
usd_data = self._get_currency_data(expense_projected)
self.assertIsNotNone(usd_data)
self.assertEqual(usd_data["expense_projected"], Decimal("150.00"))
def test_monthly_summary_description_filter(self):
"""With description filter, summary should only include matching transactions"""
response = self.client.get(
"/monthly/12/2025/summary/?description=Income",
HTTP_HX_REQUEST="true",
)
context = response.context
# Only income matches "Income" description
income_current = context.get("income_current", {})
usd_data = self._get_currency_data(income_current)
self.assertIsNotNone(usd_data)
self.assertEqual(usd_data["income_current"], Decimal("1000.00"))
# Expenses should be filtered out
expense_current = context.get("expense_current", {})
usd_data = self._get_currency_data(expense_current)
if usd_data:
self.assertEqual(usd_data.get("expense_current", 0), Decimal("0"))
def test_monthly_summary_amount_filter(self):
"""With amount filter, summary should only include transactions in range"""
# Filter to only get transactions between 100 and 250 (should get 200 and 150)
response = self.client.get(
"/monthly/12/2025/summary/?from_amount=100&to_amount=250",
HTTP_HX_REQUEST="true",
)
context = response.context
# Income (1000) should be filtered out
income_current = context.get("income_current", {})
usd_data = self._get_currency_data(income_current)
if usd_data:
self.assertEqual(usd_data.get("income_current", 0), Decimal("0"))
# expense_current should have 200
expense_current = context.get("expense_current", {})
usd_data = self._get_currency_data(expense_current)
self.assertIsNotNone(usd_data)
self.assertEqual(usd_data["expense_current"], Decimal("200.00"))
# expense_projected should have 150
expense_projected = context.get("expense_projected", {})
usd_data = self._get_currency_data(expense_projected)
self.assertIsNotNone(usd_data)
self.assertEqual(usd_data["expense_projected"], Decimal("150.00"))
# --- monthly_account_summary view tests ---
def test_monthly_account_summary_no_filter_returns_200(self):
"""Test that monthly_account_summary returns 200 without filters"""
response = self.client.get(
"/monthly/12/2025/summary/accounts/",
HTTP_HX_REQUEST="true",
)
self.assertEqual(response.status_code, 200)
def test_monthly_account_summary_with_filter_returns_200(self):
"""Test that monthly_account_summary returns 200 with filter"""
response = self.client.get(
"/monthly/12/2025/summary/accounts/?type=IN",
HTTP_HX_REQUEST="true",
)
self.assertEqual(response.status_code, 200)
# --- monthly_currency_summary view tests ---
def test_monthly_currency_summary_no_filter_returns_200(self):
"""Test that monthly_currency_summary returns 200 without filters"""
response = self.client.get(
"/monthly/12/2025/summary/currencies/",
HTTP_HX_REQUEST="true",
)
self.assertEqual(response.status_code, 200)
def test_monthly_currency_summary_with_filter_returns_200(self):
"""Test that monthly_currency_summary returns 200 with filter"""
response = self.client.get(
"/monthly/12/2025/summary/currencies/?type=EX",
HTTP_HX_REQUEST="true",
)
self.assertEqual(response.status_code, 200)

View File

@@ -2,7 +2,8 @@ from django.contrib.auth.decorators import login_required
from django.db.models import (
Q,
)
from django.http import HttpResponse
from django.http import HttpResponse, Http404
from django.shortcuts import render, redirect
from django.utils import timezone
from django.views.decorators.http import require_http_methods
@@ -36,8 +37,6 @@ def monthly_overview(request, month: int, year: int):
summary_tab = request.session.get("monthly_summary_tab", "summary")
if month < 1 or month > 12:
from django.http import Http404
raise Http404("Month is out of range")
next_month = 1 if month == 12 else month + 1
@@ -76,6 +75,8 @@ def transactions_list(request, month: int, year: int):
if order != request.session.get("monthly_transactions_order", "default"):
request.session["monthly_transactions_order"] = order
today = timezone.localdate(timezone.now())
f = TransactionsFilter(request.GET)
transactions_filtered = f.qs.filter(
reference_date__year=year,
@@ -93,12 +94,28 @@ def transactions_list(request, month: int, year: int):
"dca_income_entries",
)
# Late transactions: date < today and is_paid = False (only shown for default ordering)
late_transactions = None
if order == "default":
late_transactions = transactions_filtered.filter(
date__lt=today,
is_paid=False,
).order_by("date", "id")
# Exclude late transactions from the main list
transactions_filtered = transactions_filtered.exclude(
date__lt=today,
is_paid=False,
)
transactions_filtered = default_order(transactions_filtered, order=order)
return render(
request,
"monthly_overview/fragments/list.html",
context={"transactions": transactions_filtered},
context={
"transactions": transactions_filtered,
"late_transactions": late_transactions,
},
)
@@ -107,17 +124,48 @@ def transactions_list(request, month: int, year: int):
@require_http_methods(["GET"])
def monthly_summary(request, month: int, year: int):
# Base queryset with all required filters
base_queryset = (
Transaction.objects.filter(
reference_date__year=year,
reference_date__month=month,
account__is_asset=False,
)
.exclude(Q(Q(category__mute=True) & ~Q(category=None)) | Q(mute=True))
.exclude(account__in=request.user.untracked_accounts.all())
base_queryset = Transaction.objects.filter(
reference_date__year=year,
reference_date__month=month,
)
data = calculate_currency_totals(base_queryset, ignore_empty=True)
# Apply filters and check if any are active
f = TransactionsFilter(request.GET, queryset=base_queryset)
# Check if any filter has a non-default value
# Default values are: type=['IN', 'EX'], is_paid=['1', '0'], everything else empty
has_active_filter = False
if f.form.is_valid():
for name, value in f.form.cleaned_data.items():
# Skip fields with default/empty values
if not value:
continue
# Skip type if it has both default values
if name == "type" and set(value) == {"IN", "EX"}:
continue
# Skip is_paid if it has both default values (values are strings)
if name == "is_paid" and set(value) == {"1", "0"}:
continue
# Skip mute_status if it has both default values
if name == "mute_status" and set(value) == {"active", "muted"}:
continue
# If we get here, there's an active filter
has_active_filter = True
break
if has_active_filter:
queryset = f.qs
else:
queryset = (
base_queryset.exclude(
Q(Q(category__mute=True) & ~Q(category=None)) | Q(mute=True)
)
.exclude(account__in=request.user.untracked_accounts.all())
.exclude(account__is_asset=True)
)
data = calculate_currency_totals(queryset, ignore_empty=True)
percentages = calculate_percentage_distribution(data)
context = {
@@ -132,6 +180,7 @@ def monthly_summary(request, month: int, year: int):
currency_totals=data, month=month, year=year
),
"percentages": percentages,
"has_active_filter": has_active_filter,
}
return render(
@@ -149,9 +198,38 @@ def monthly_account_summary(request, month: int, year: int):
base_queryset = Transaction.objects.filter(
reference_date__year=year,
reference_date__month=month,
).exclude(Q(Q(category__mute=True) & ~Q(category=None)) | Q(mute=True))
)
account_data = calculate_account_totals(transactions_queryset=base_queryset.all())
# Apply filters and check if any are active
f = TransactionsFilter(request.GET, queryset=base_queryset)
# Check if any filter has a non-default value
has_active_filter = False
if f.form.is_valid():
for name, value in f.form.cleaned_data.items():
if not value:
continue
if name == "type" and set(value) == {"IN", "EX"}:
continue
if name == "is_paid" and set(value) == {"1", "0"}:
continue
if name == "mute_status" and set(value) == {"active", "muted"}:
continue
has_active_filter = True
break
if has_active_filter:
queryset = f.qs
else:
queryset = (
base_queryset.exclude(
Q(Q(category__mute=True) & ~Q(category=None)) | Q(mute=True)
)
.exclude(account__in=request.user.untracked_accounts.all())
.exclude(account__is_asset=True)
)
account_data = calculate_account_totals(transactions_queryset=queryset.all())
account_percentages = calculate_percentage_distribution(account_data)
context = {
@@ -171,16 +249,41 @@ def monthly_account_summary(request, month: int, year: int):
@require_http_methods(["GET"])
def monthly_currency_summary(request, month: int, year: int):
# Base queryset with all required filters
base_queryset = (
Transaction.objects.filter(
reference_date__year=year,
reference_date__month=month,
)
.exclude(Q(Q(category__mute=True) & ~Q(category=None)) | Q(mute=True))
.exclude(account__in=request.user.untracked_accounts.all())
base_queryset = Transaction.objects.filter(
reference_date__year=year,
reference_date__month=month,
)
currency_data = calculate_currency_totals(base_queryset.all(), ignore_empty=True)
# Apply filters and check if any are active
f = TransactionsFilter(request.GET, queryset=base_queryset)
# Check if any filter has a non-default value
has_active_filter = False
if f.form.is_valid():
for name, value in f.form.cleaned_data.items():
if not value:
continue
if name == "type" and set(value) == {"IN", "EX"}:
continue
if name == "is_paid" and set(value) == {"1", "0"}:
continue
if name == "mute_status" and set(value) == {"active", "muted"}:
continue
has_active_filter = True
break
if has_active_filter:
queryset = f.qs
else:
queryset = (
base_queryset.exclude(
Q(Q(category__mute=True) & ~Q(category=None)) | Q(mute=True)
)
.exclude(account__in=request.user.untracked_accounts.all())
.exclude(account__is_asset=True)
)
currency_data = calculate_currency_totals(queryset.all(), ignore_empty=True)
currency_percentages = calculate_percentage_distribution(currency_data)
context = {

View File

@@ -23,6 +23,11 @@ SITUACAO_CHOICES = (
("0", _("Projected")),
)
MUTE_STATUS_CHOICES = (
("active", _("Active")),
("muted", _("Muted")),
)
def content_filter(queryset, name, value):
queryset = queryset.filter(
@@ -78,6 +83,11 @@ class TransactionsFilter(django_filters.FilterSet):
choices=SITUACAO_CHOICES,
field_name="is_paid",
)
mute_status = django_filters.MultipleChoiceFilter(
choices=MUTE_STATUS_CHOICES,
method="filter_mute_status",
label=_("Mute Status"),
)
date_start = django_filters.DateFilter(
field_name="date",
lookup_expr="gte",
@@ -140,6 +150,9 @@ class TransactionsFilter(django_filters.FilterSet):
if data.get("is_paid") is None:
data.setlist("is_paid", ["1", "0"])
if data.get("mute_status") is None:
data.setlist("mute_status", ["active", "muted"])
super().__init__(data, *args, **kwargs)
self.form.helper = FormHelper()
@@ -155,6 +168,10 @@ class TransactionsFilter(django_filters.FilterSet):
"is_paid",
template="transactions/widgets/transaction_type_filter_buttons.html",
),
Field(
"mute_status",
template="transactions/widgets/transaction_type_filter_buttons.html",
),
Field("description"),
Row(Column("date_start"), Column("date_end")),
Row(
@@ -268,3 +285,36 @@ class TransactionsFilter(django_filters.FilterSet):
return queryset.filter(q).distinct()
return queryset
@staticmethod
def filter_mute_status(queryset, name, value):
from apps.common.middleware.thread_local import get_current_user
if not value:
return queryset
value = list(value)
# If both are selected, return all
if "active" in value and "muted" in value:
return queryset
user = get_current_user()
# Only Active selected: exclude muted transactions
if "active" in value:
return (
queryset.exclude(account__untracked_by=user)
.filter(
mute=False,
)
.filter(Q(category__mute=False) | Q(category__isnull=True))
)
# Only Muted selected: include only muted transactions
if "muted" in value:
return queryset.filter(
Q(account__untracked_by=user) | Q(category__mute=True) | Q(mute=True)
)
return queryset

View File

@@ -383,6 +383,10 @@ class Transaction(OwnedObject):
def clean(self):
super().clean()
# Convert empty internal_id to None to allow multiple "empty" values with unique constraint
if self.internal_id == "":
self.internal_id = None
# Only process amount and reference_date if account exists
# If account is missing, Django's required field validation will handle it
try:

View File

@@ -125,6 +125,70 @@ class TransactionTests(TestCase):
datetime.datetime(day=1, month=2, year=2000).date(),
)
def test_empty_internal_id_converts_to_none(self):
"""Test that empty string internal_id is converted to None"""
transaction = Transaction.objects.create(
account=self.account,
type=Transaction.Type.EXPENSE,
date=timezone.now().date(),
amount=Decimal("100.00"),
description="Test transaction",
internal_id="", # Empty string should become None
)
self.assertIsNone(transaction.internal_id)
def test_unique_internal_id_works(self):
"""Test that unique non-empty internal_id values work correctly"""
transaction1 = Transaction.objects.create(
account=self.account,
type=Transaction.Type.EXPENSE,
date=timezone.now().date(),
amount=Decimal("100.00"),
description="Test transaction 1",
internal_id="unique-id-123",
)
transaction2 = Transaction.objects.create(
account=self.account,
type=Transaction.Type.EXPENSE,
date=timezone.now().date(),
amount=Decimal("100.00"),
description="Test transaction 2",
internal_id="unique-id-456",
)
self.assertEqual(transaction1.internal_id, "unique-id-123")
self.assertEqual(transaction2.internal_id, "unique-id-456")
def test_multiple_transactions_with_empty_internal_id(self):
"""Test that multiple transactions can have empty/None internal_id"""
transaction1 = Transaction.objects.create(
account=self.account,
type=Transaction.Type.EXPENSE,
date=timezone.now().date(),
amount=Decimal("100.00"),
description="Test transaction 1",
internal_id="",
)
transaction2 = Transaction.objects.create(
account=self.account,
type=Transaction.Type.EXPENSE,
date=timezone.now().date(),
amount=Decimal("100.00"),
description="Test transaction 2",
internal_id="",
)
transaction3 = Transaction.objects.create(
account=self.account,
type=Transaction.Type.EXPENSE,
date=timezone.now().date(),
amount=Decimal("100.00"),
description="Test transaction 3",
internal_id=None,
)
# All should be saved successfully with None internal_id
self.assertIsNone(transaction1.internal_id)
self.assertIsNone(transaction2.internal_id)
self.assertIsNone(transaction3.internal_id)
class InstallmentPlanTests(TestCase):
def setUp(self):

View File

@@ -35,7 +35,7 @@ def categories_list(request):
@login_required
@require_http_methods(["GET"])
def categories_table_active(request):
categories = TransactionCategory.objects.filter(active=True).order_by("id")
categories = TransactionCategory.objects.filter(active=True).order_by("name")
return render(
request,
"categories/fragments/table.html",
@@ -47,7 +47,7 @@ def categories_table_active(request):
@login_required
@require_http_methods(["GET"])
def categories_table_archived(request):
categories = TransactionCategory.objects.filter(active=False).order_by("id")
categories = TransactionCategory.objects.filter(active=False).order_by("name")
return render(
request,
"categories/fragments/table.html",

View File

@@ -35,7 +35,7 @@ def entities_list(request):
@login_required
@require_http_methods(["GET"])
def entities_table_active(request):
entities = TransactionEntity.objects.filter(active=True).order_by("id")
entities = TransactionEntity.objects.filter(active=True).order_by("name")
return render(
request,
"entities/fragments/table.html",
@@ -47,7 +47,7 @@ def entities_table_active(request):
@login_required
@require_http_methods(["GET"])
def entities_table_archived(request):
entities = TransactionEntity.objects.filter(active=False).order_by("id")
entities = TransactionEntity.objects.filter(active=False).order_by("name")
return render(
request,
"entities/fragments/table.html",

View File

@@ -35,7 +35,7 @@ def tags_list(request):
@login_required
@require_http_methods(["GET"])
def tags_table_active(request):
tags = TransactionTag.objects.filter(active=True).order_by("id")
tags = TransactionTag.objects.filter(active=True).order_by("name")
return render(
request,
"tags/fragments/table.html",
@@ -47,7 +47,7 @@ def tags_table_active(request):
@login_required
@require_http_methods(["GET"])
def tags_table_archived(request):
tags = TransactionTag.objects.filter(active=False).order_by("id")
tags = TransactionTag.objects.filter(active=False).order_by("name")
return render(
request,
"tags/fragments/table.html",

View File

@@ -152,7 +152,9 @@ def transaction_simple_add(request):
date_param = request.GET.get("date")
if date_param:
try:
initial_data["date"] = datetime.datetime.strptime(date_param, "%Y-%m-%d").date()
initial_data["date"] = datetime.datetime.strptime(
date_param, "%Y-%m-%d"
).date()
except ValueError:
pass
@@ -160,7 +162,9 @@ def transaction_simple_add(request):
reference_date_param = request.GET.get("reference_date")
if reference_date_param:
try:
initial_data["reference_date"] = datetime.datetime.strptime(reference_date_param, "%Y-%m-%d").date()
initial_data["reference_date"] = datetime.datetime.strptime(
reference_date_param, "%Y-%m-%d"
).date()
except ValueError:
pass
@@ -172,7 +176,10 @@ def transaction_simple_add(request):
except (ValueError, TypeError):
# Try to find by name
from apps.accounts.models import Account
account = Account.objects.filter(name__iexact=account_param, is_archived=False).first()
account = Account.objects.filter(
name__iexact=account_param, is_archived=False
).first()
if account:
initial_data["account"] = account.pk
@@ -207,7 +214,10 @@ def transaction_simple_add(request):
except (ValueError, TypeError):
# Try to find by name
from apps.transactions.models import TransactionCategory
category = TransactionCategory.objects.filter(name__iexact=category_param, active=True).first()
category = TransactionCategory.objects.filter(
name__iexact=category_param, active=True
).first()
if category:
initial_data["category"] = category.pk
@@ -457,7 +467,7 @@ def transaction_pay(request, transaction_id):
context={"transaction": transaction, **request.GET},
)
response.headers["HX-Trigger"] = (
f'{"paid" if new_is_paid else "unpaid"}, selective_update'
f"{'paid' if new_is_paid else 'unpaid'}, selective_update"
)
return response
@@ -552,6 +562,8 @@ def transaction_all_list(request):
if order != request.session.get("all_transactions_order", "default"):
request.session["all_transactions_order"] = order
today = timezone.localdate(timezone.now())
transactions = Transaction.objects.prefetch_related(
"account",
"account__group",
@@ -565,12 +577,27 @@ def transaction_all_list(request):
"dca_income_entries",
).all()
transactions = default_order(transactions, order=order)
f = TransactionsFilter(request.GET, queryset=transactions)
# Late transactions: date < today and is_paid = False (only shown for default ordering on first page)
late_transactions = None
page_number = request.GET.get("page", 1)
paginator = Paginator(f.qs, 100)
if order == "default" and str(page_number) == "1":
late_transactions = f.qs.filter(
date__lt=today,
is_paid=False,
).order_by("date", "id")
# Exclude late transactions from the main paginated list
main_transactions = f.qs.exclude(
date__lt=today,
is_paid=False,
)
else:
main_transactions = f.qs
main_transactions = default_order(main_transactions, order=order)
paginator = Paginator(main_transactions, 100)
page_obj = paginator.get_page(page_number)
return render(
@@ -579,6 +606,7 @@ def transaction_all_list(request):
{
"page_obj": page_obj,
"paginator": paginator,
"late_transactions": late_transactions,
},
)

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-12-20 02:59+0000\n"
"POT-Creation-Date: 2026-01-10 20:50+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"
@@ -66,24 +66,30 @@ msgstr ""
#: apps/transactions/forms.py:419 apps/transactions/forms.py:516
#: apps/transactions/forms.py:523 apps/transactions/forms.py:707
#: apps/transactions/forms.py:948 apps/transactions/models.py:322
#: apps/transactions/models.py:574 apps/transactions/models.py:774
#: apps/transactions/models.py:1022
#: apps/transactions/models.py:578 apps/transactions/models.py:778
#: apps/transactions/models.py:1026
#: templates/insights/fragments/category_overview/index.html:86
#: templates/insights/fragments/category_overview/index.html:542
#: templates/insights/fragments/month_by_month.html:84
#: templates/insights/fragments/year_by_year.html:52
msgid "Category"
msgstr ""
#: apps/accounts/forms.py:132 apps/dca/forms.py:95 apps/dca/forms.py:103
#: apps/export_app/forms.py:43 apps/export_app/forms.py:132
#: apps/rules/forms.py:184 apps/rules/forms.py:194 apps/rules/models.py:45
#: apps/rules/models.py:315 apps/transactions/filters.py:68
#: apps/rules/models.py:315 apps/transactions/filters.py:73
#: apps/transactions/forms.py:51 apps/transactions/forms.py:259
#: apps/transactions/forms.py:427 apps/transactions/forms.py:532
#: apps/transactions/forms.py:540 apps/transactions/forms.py:700
#: apps/transactions/forms.py:941 apps/transactions/models.py:328
#: apps/transactions/models.py:576 apps/transactions/models.py:778
#: apps/transactions/models.py:1028 templates/includes/sidebar.html:150
#: apps/transactions/models.py:580 apps/transactions/models.py:782
#: apps/transactions/models.py:1032 templates/includes/sidebar.html:150
#: templates/insights/fragments/category_overview/index.html:40
#: templates/insights/fragments/month_by_month.html:29
#: templates/insights/fragments/month_by_month.html:32
#: templates/insights/fragments/year_by_year.html:24
#: templates/insights/fragments/year_by_year.html:27
#: templates/tags/fragments/list.html:9 templates/tags/pages/index.html:4
msgid "Tags"
msgstr ""
@@ -91,7 +97,7 @@ msgstr ""
#: apps/accounts/models.py:12 apps/accounts/models.py:29 apps/dca/models.py:13
#: apps/import_app/models.py:14 apps/rules/models.py:13
#: apps/transactions/models.py:214 apps/transactions/models.py:239
#: apps/transactions/models.py:263 apps/transactions/models.py:990
#: apps/transactions/models.py:263 apps/transactions/models.py:994
#: templates/account_groups/fragments/list.html:22
#: templates/accounts/fragments/list.html:22
#: templates/categories/fragments/table.html:17
@@ -160,8 +166,8 @@ msgstr ""
#: apps/transactions/forms.py:63 apps/transactions/forms.py:271
#: apps/transactions/forms.py:386 apps/transactions/forms.py:692
#: apps/transactions/forms.py:933 apps/transactions/models.py:294
#: apps/transactions/models.py:534 apps/transactions/models.py:756
#: apps/transactions/models.py:996
#: apps/transactions/models.py:538 apps/transactions/models.py:760
#: apps/transactions/models.py:1000
#: templates/installment_plans/fragments/table.html:17
#: templates/quick_transactions/fragments/list.html:14
#: templates/recurring_transactions/fragments/table.html:19
@@ -170,11 +176,11 @@ msgid "Account"
msgstr ""
#: apps/accounts/models.py:76 apps/export_app/forms.py:19
#: apps/export_app/forms.py:129 apps/transactions/filters.py:52
#: apps/export_app/forms.py:129 apps/transactions/filters.py:57
#: templates/accounts/fragments/list.html:9
#: templates/accounts/pages/index.html:4 templates/includes/sidebar.html:162
#: templates/includes/sidebar.html:164
#: templates/monthly_overview/pages/overview.html:75
#: templates/monthly_overview/pages/overview.html:77
#: templates/transactions/pages/transactions.html:26
msgid "Accounts"
msgstr ""
@@ -189,8 +195,8 @@ msgstr ""
#: apps/accounts/views/account_groups.py:69
#: apps/accounts/views/account_groups.py:152 apps/accounts/views/accounts.py:68
#: apps/accounts/views/accounts.py:106 apps/dca/views.py:63
#: apps/dca/views.py:146 apps/rules/views.py:118 apps/rules/views.py:228
#: apps/accounts/views/accounts.py:106 apps/dca/views.py:62
#: apps/dca/views.py:145 apps/rules/views.py:118 apps/rules/views.py:228
#: apps/transactions/views/categories.py:91
#: apps/transactions/views/categories.py:129
#: apps/transactions/views/entities.py:91
@@ -204,7 +210,7 @@ msgid "Account Group updated successfully"
msgstr ""
#: apps/accounts/views/account_groups.py:111
#: apps/accounts/views/accounts.py:145 apps/dca/views.py:105
#: apps/accounts/views/accounts.py:145 apps/dca/views.py:104
#: apps/rules/views.py:185 apps/transactions/views/categories.py:168
#: apps/transactions/views/entities.py:130 apps/transactions/views/tags.py:130
msgid "Item no longer shared with you"
@@ -215,14 +221,14 @@ msgid "Account Group deleted successfully"
msgstr ""
#: apps/accounts/views/account_groups.py:135
#: apps/accounts/views/accounts.py:189 apps/dca/views.py:129
#: apps/accounts/views/accounts.py:189 apps/dca/views.py:128
#: apps/rules/views.py:210 apps/transactions/views/categories.py:192
#: apps/transactions/views/entities.py:154 apps/transactions/views/tags.py:154
msgid "Ownership taken successfully"
msgstr ""
#: apps/accounts/views/account_groups.py:165
#: apps/accounts/views/accounts.py:119 apps/dca/views.py:159
#: apps/accounts/views/accounts.py:119 apps/dca/views.py:158
#: apps/rules/views.py:241 apps/transactions/views/categories.py:142
#: apps/transactions/views/entities.py:184 apps/transactions/views/tags.py:184
msgid "Configuration saved successfully"
@@ -355,7 +361,7 @@ msgid "Public"
msgstr ""
#: apps/common/templatetags/natural.py:20
#: templates/monthly_overview/fragments/monthly_summary.html:9
#: templates/monthly_overview/fragments/monthly_summary.html:10
msgid "today"
msgstr ""
@@ -453,10 +459,10 @@ msgstr ""
#: apps/common/widgets/tom_select.py:15
#: templates/mini_tools/unit_price_calculator.html:180
#: templates/monthly_overview/pages/overview.html:171
#: templates/monthly_overview/pages/overview.html:183
#: templates/transactions/pages/transactions.html:124
#: templates/transactions/pages/transactions.html:136
#: templates/monthly_overview/pages/overview.html:275
#: templates/monthly_overview/pages/overview.html:287
#: templates/transactions/pages/transactions.html:225
#: templates/transactions/pages/transactions.html:237
msgid "Clear"
msgstr ""
@@ -495,11 +501,11 @@ msgid "Decimal Places"
msgstr ""
#: apps/currencies/models.py:45 apps/export_app/forms.py:25
#: apps/export_app/forms.py:130 apps/transactions/filters.py:59
#: apps/export_app/forms.py:130 apps/transactions/filters.py:64
#: templates/currencies/fragments/list.html:9
#: templates/currencies/pages/index.html:4 templates/includes/sidebar.html:176
#: templates/includes/sidebar.html:178
#: templates/monthly_overview/pages/overview.html:62
#: templates/monthly_overview/pages/overview.html:63
#: templates/transactions/pages/transactions.html:12
msgid "Currencies"
msgstr ""
@@ -560,9 +566,9 @@ msgstr ""
msgid "Service Type"
msgstr ""
#: apps/currencies/models.py:118 apps/transactions/models.py:218
#: apps/transactions/models.py:242 apps/transactions/models.py:266
#: templates/categories/fragments/list.html:16
#: apps/currencies/models.py:118 apps/transactions/filters.py:27
#: apps/transactions/models.py:218 apps/transactions/models.py:242
#: apps/transactions/models.py:266 templates/categories/fragments/list.html:16
#: templates/entities/fragments/list.html:16
#: templates/installment_plans/fragments/list.html:16
#: templates/recurring_transactions/fragments/list.html:16
@@ -590,57 +596,57 @@ msgstr ""
msgid "Last Successful Fetch"
msgstr ""
#: apps/currencies/models.py:141
#: apps/currencies/models.py:143
msgid "Target Currencies"
msgstr ""
#: apps/currencies/models.py:143
#: apps/currencies/models.py:145
msgid ""
"Select currencies to fetch exchange rates for. Rates will be fetched for "
"each currency against their set exchange currency."
msgstr ""
#: apps/currencies/models.py:151
#: apps/currencies/models.py:153
msgid "Target Accounts"
msgstr ""
#: apps/currencies/models.py:153
#: apps/currencies/models.py:155
msgid ""
"Select accounts to fetch exchange rates for. Rates will be fetched for each "
"account's currency against their set exchange currency."
msgstr ""
#: apps/currencies/models.py:160
#: apps/currencies/models.py:162
msgid "Single exchange rate"
msgstr ""
#: apps/currencies/models.py:163
#: apps/currencies/models.py:165
msgid "Create one exchange rate and keep updating it. Avoids database clutter."
msgstr ""
#: apps/currencies/models.py:168
#: apps/currencies/models.py:170
msgid "Exchange Rate Service"
msgstr ""
#: apps/currencies/models.py:169
#: apps/currencies/models.py:171
msgid "Exchange Rate Services"
msgstr ""
#: apps/currencies/models.py:221
#: apps/currencies/models.py:223
msgid "'Every X hours' interval type requires a positive integer."
msgstr ""
#: apps/currencies/models.py:230
#: apps/currencies/models.py:232
msgid "'Every X hours' interval must be between 1 and 24."
msgstr ""
#: apps/currencies/models.py:244
#: apps/currencies/models.py:246
msgid ""
"Invalid hour format. Use comma-separated hours (0-23) and/or ranges (e.g., "
"'1-5,8,10-12')."
msgstr ""
#: apps/currencies/models.py:255
#: apps/currencies/models.py:257
msgid ""
"Invalid format. Please check the requirements for your selected interval "
"type."
@@ -739,8 +745,8 @@ msgstr ""
#: apps/dca/models.py:26 apps/dca/models.py:181 apps/rules/forms.py:180
#: apps/rules/forms.py:196 apps/rules/models.py:43 apps/rules/models.py:295
#: apps/transactions/forms.py:413 apps/transactions/forms.py:560
#: apps/transactions/models.py:318 apps/transactions/models.py:583
#: apps/transactions/models.py:784 apps/transactions/models.py:1018
#: apps/transactions/models.py:318 apps/transactions/models.py:587
#: apps/transactions/models.py:788 apps/transactions/models.py:1022
msgid "Notes"
msgstr ""
@@ -772,27 +778,27 @@ msgstr ""
msgid "DCA Entries"
msgstr ""
#: apps/dca/views.py:39
#: apps/dca/views.py:38
msgid "DCA Strategy added successfully"
msgstr ""
#: apps/dca/views.py:76
#: apps/dca/views.py:75
msgid "DCA Strategy updated successfully"
msgstr ""
#: apps/dca/views.py:108
#: apps/dca/views.py:107
msgid "DCA strategy deleted successfully"
msgstr ""
#: apps/dca/views.py:238
#: apps/dca/views.py:237
msgid "Entry added successfully"
msgstr ""
#: apps/dca/views.py:265
#: apps/dca/views.py:264
msgid "Entry updated successfully"
msgstr ""
#: apps/dca/views.py:291
#: apps/dca/views.py:290
msgid "Entry deleted successfully"
msgstr ""
@@ -812,34 +818,42 @@ msgid "Transactions"
msgstr ""
#: apps/export_app/forms.py:37 apps/export_app/forms.py:131
#: apps/transactions/filters.py:63 templates/categories/fragments/list.html:9
#: apps/transactions/filters.py:68 templates/categories/fragments/list.html:9
#: templates/categories/pages/index.html:4 templates/includes/sidebar.html:144
#: templates/insights/fragments/month_by_month.html:18
#: templates/insights/fragments/month_by_month.html:21
#: templates/insights/fragments/year_by_year.html:13
#: templates/insights/fragments/year_by_year.html:16
msgid "Categories"
msgstr ""
#: apps/export_app/forms.py:49 apps/export_app/forms.py:133
#: apps/rules/forms.py:185 apps/rules/forms.py:195 apps/rules/models.py:46
#: apps/rules/models.py:307 apps/transactions/filters.py:73
#: apps/rules/models.py:307 apps/transactions/filters.py:78
#: apps/transactions/forms.py:59 apps/transactions/forms.py:267
#: apps/transactions/forms.py:435 apps/transactions/forms.py:715
#: apps/transactions/forms.py:956 apps/transactions/models.py:277
#: apps/transactions/models.py:333 apps/transactions/models.py:579
#: apps/transactions/models.py:781 apps/transactions/models.py:1033
#: apps/transactions/models.py:333 apps/transactions/models.py:583
#: apps/transactions/models.py:785 apps/transactions/models.py:1037
#: templates/entities/fragments/list.html:9
#: templates/entities/pages/index.html:4 templates/includes/sidebar.html:156
#: templates/insights/fragments/category_overview/index.html:54
#: templates/insights/fragments/month_by_month.html:40
#: templates/insights/fragments/month_by_month.html:43
#: templates/insights/fragments/year_by_year.html:35
#: templates/insights/fragments/year_by_year.html:38
msgid "Entities"
msgstr ""
#: apps/export_app/forms.py:55 apps/export_app/forms.py:137
#: apps/transactions/models.py:821 templates/includes/sidebar.html:110
#: apps/transactions/models.py:825 templates/includes/sidebar.html:110
#: templates/recurring_transactions/fragments/list.html:9
#: templates/recurring_transactions/pages/index.html:4
msgid "Recurring Transactions"
msgstr ""
#: apps/export_app/forms.py:61 apps/export_app/forms.py:135
#: apps/transactions/models.py:597 templates/includes/sidebar.html:104
#: apps/transactions/models.py:601 templates/includes/sidebar.html:104
#: templates/installment_plans/fragments/list.html:9
#: templates/installment_plans/pages/index.html:4
msgid "Installment Plans"
@@ -991,10 +1005,12 @@ msgid "Run deleted successfully"
msgstr ""
#: apps/insights/forms.py:118 apps/insights/utils/sankey.py:36
#: apps/insights/utils/sankey.py:167 apps/transactions/filters.py:186
#: apps/insights/utils/sankey.py:167 apps/transactions/filters.py:203
#: templates/insights/fragments/category_overview/index.html:96
#: templates/insights/fragments/category_overview/index.html:407
#: templates/insights/fragments/category_overview/index.html:436
#: templates/insights/fragments/month_by_month.html:119
#: templates/insights/fragments/year_by_year.html:73
msgid "Uncategorized"
msgstr ""
@@ -1087,15 +1103,15 @@ msgstr ""
#: apps/rules/forms.py:174 apps/rules/forms.py:188 apps/rules/models.py:36
#: apps/rules/models.py:271 apps/transactions/forms.py:377
#: apps/transactions/models.py:301 apps/transactions/models.py:539
#: apps/transactions/models.py:762 apps/transactions/models.py:1003
#: apps/transactions/models.py:301 apps/transactions/models.py:543
#: apps/transactions/models.py:766 apps/transactions/models.py:1007
msgid "Type"
msgstr ""
#: apps/rules/forms.py:175 apps/rules/forms.py:189 apps/rules/models.py:37
#: apps/rules/models.py:275 apps/transactions/filters.py:22
#: apps/transactions/forms.py:381 apps/transactions/models.py:303
#: apps/transactions/models.py:1005 templates/cotton/transaction/item.html:20
#: apps/transactions/models.py:1009 templates/cotton/transaction/item.html:20
#: templates/cotton/transaction/item.html:31
#: templates/transactions/widgets/paid_toggle_button.html:10
#: templates/transactions/widgets/unselectable_paid_toggle_button.html:13
@@ -1106,14 +1122,14 @@ msgstr ""
#: apps/rules/models.py:283 apps/transactions/forms.py:71
#: apps/transactions/forms.py:397 apps/transactions/forms.py:547
#: apps/transactions/forms.py:721 apps/transactions/models.py:305
#: apps/transactions/models.py:557 apps/transactions/models.py:786
#: apps/transactions/models.py:561 apps/transactions/models.py:790
msgid "Reference Date"
msgstr ""
#: apps/rules/forms.py:178 apps/rules/forms.py:192 apps/rules/models.py:41
#: apps/rules/models.py:287 apps/transactions/forms.py:404
#: apps/transactions/models.py:311 apps/transactions/models.py:767
#: apps/transactions/models.py:1011
#: apps/transactions/models.py:311 apps/transactions/models.py:771
#: apps/transactions/models.py:1015
#: templates/insights/fragments/sankey.html:102
#: templates/installment_plans/fragments/table.html:18
#: templates/quick_transactions/fragments/list.html:15
@@ -1124,27 +1140,27 @@ msgstr ""
#: apps/rules/forms.py:179 apps/rules/forms.py:193 apps/rules/models.py:14
#: apps/rules/models.py:42 apps/rules/models.py:291
#: apps/transactions/forms.py:408 apps/transactions/forms.py:551
#: apps/transactions/models.py:316 apps/transactions/models.py:541
#: apps/transactions/models.py:770 apps/transactions/models.py:1016
#: apps/transactions/models.py:316 apps/transactions/models.py:545
#: apps/transactions/models.py:774 apps/transactions/models.py:1020
msgid "Description"
msgstr ""
#: apps/rules/forms.py:182 apps/rules/forms.py:198 apps/rules/models.py:47
#: apps/rules/models.py:299 apps/transactions/models.py:355
#: apps/transactions/models.py:1038
#: apps/transactions/models.py:1042
msgid "Internal Note"
msgstr ""
#: apps/rules/forms.py:183 apps/rules/forms.py:199 apps/rules/models.py:48
#: apps/rules/models.py:303 apps/transactions/models.py:357
#: apps/transactions/models.py:1040
#: apps/transactions/models.py:1044
msgid "Internal ID"
msgstr ""
#: apps/rules/forms.py:186 apps/rules/forms.py:200 apps/rules/models.py:40
#: apps/rules/models.py:319 apps/transactions/forms.py:564
#: apps/transactions/models.py:215 apps/transactions/models.py:306
#: apps/transactions/models.py:1006
#: apps/transactions/models.py:1010
msgid "Mute"
msgstr ""
@@ -1300,53 +1316,65 @@ msgstr ""
msgid "Projected"
msgstr ""
#: apps/transactions/filters.py:40
#: apps/transactions/filters.py:28 templates/categories/fragments/table.html:18
msgid "Muted"
msgstr ""
#: apps/transactions/filters.py:45
msgid "Content"
msgstr ""
#: apps/transactions/filters.py:46
#: apps/transactions/filters.py:51
msgid "Transaction Type"
msgstr ""
#: apps/transactions/filters.py:84
msgid "Date from"
msgstr ""
#: apps/transactions/filters.py:89 apps/transactions/filters.py:99
msgid "Until"
#: apps/transactions/filters.py:89
msgid "Mute Status"
msgstr ""
#: apps/transactions/filters.py:94
msgid "Reference date from"
msgid "Date from"
msgstr ""
#: apps/transactions/filters.py:99 apps/transactions/filters.py:109
msgid "Until"
msgstr ""
#: apps/transactions/filters.py:104
msgid "Reference date from"
msgstr ""
#: apps/transactions/filters.py:114
msgid "Amount min"
msgstr ""
#: apps/transactions/filters.py:109
#: apps/transactions/filters.py:119
msgid "Amount max"
msgstr ""
#: apps/transactions/filters.py:185
#: apps/transactions/filters.py:202
msgid "Categorized"
msgstr ""
#: apps/transactions/filters.py:192
#: apps/transactions/filters.py:209
msgid "Tagged"
msgstr ""
#: apps/transactions/filters.py:192
#: apps/transactions/filters.py:209
#: templates/insights/fragments/category_overview/index.html:189
#: templates/insights/fragments/month_by_month.html:121
#: templates/insights/fragments/year_by_year.html:75
msgid "Untagged"
msgstr ""
#: apps/transactions/filters.py:198
#: apps/transactions/filters.py:215
msgid "Any entity"
msgstr ""
#: apps/transactions/filters.py:199
#: apps/transactions/filters.py:216
#: templates/insights/fragments/category_overview/index.html:282
#: templates/insights/fragments/month_by_month.html:123
#: templates/insights/fragments/year_by_year.html:77
msgid "No entity"
msgstr ""
@@ -1434,10 +1462,12 @@ msgid ""
msgstr ""
#: apps/transactions/models.py:276
#: templates/insights/fragments/month_by_month.html:88
#: templates/insights/fragments/year_by_year.html:56
msgid "Entity"
msgstr ""
#: apps/transactions/models.py:288 apps/transactions/models.py:983
#: apps/transactions/models.py:288 apps/transactions/models.py:987
#: templates/calendar_view/fragments/list.html:42
#: templates/calendar_view/fragments/list.html:44
#: templates/calendar_view/fragments/list.html:52
@@ -1445,11 +1475,11 @@ msgstr ""
#: templates/cotton/ui/quick_transactions_buttons.html:10
#: templates/cotton/ui/transactions_fab.html:10
#: templates/insights/fragments/category_overview/index.html:87
#: templates/monthly_overview/fragments/monthly_summary.html:39
#: templates/monthly_overview/fragments/monthly_summary.html:41
msgid "Income"
msgstr ""
#: apps/transactions/models.py:289 apps/transactions/models.py:984
#: apps/transactions/models.py:289 apps/transactions/models.py:988
#: templates/calendar_view/fragments/list.html:46
#: templates/calendar_view/fragments/list.html:48
#: templates/calendar_view/fragments/list.html:56
@@ -1460,11 +1490,11 @@ msgstr ""
msgid "Expense"
msgstr ""
#: apps/transactions/models.py:344 apps/transactions/models.py:596
#: apps/transactions/models.py:344 apps/transactions/models.py:600
msgid "Installment Plan"
msgstr ""
#: apps/transactions/models.py:353 apps/transactions/models.py:820
#: apps/transactions/models.py:353 apps/transactions/models.py:824
msgid "Recurring Transaction"
msgstr ""
@@ -1476,113 +1506,113 @@ msgstr ""
msgid "Deleted At"
msgstr ""
#: apps/transactions/models.py:476 templates/tags/fragments/table.html:69
#: apps/transactions/models.py:480 templates/tags/fragments/table.html:69
msgid "No tags"
msgstr ""
#: apps/transactions/models.py:478
#: apps/transactions/models.py:482
msgid "No category"
msgstr ""
#: apps/transactions/models.py:480
#: apps/transactions/models.py:484
msgid "No description"
msgstr ""
#: apps/transactions/models.py:528 templates/includes/sidebar.html:57
#: apps/transactions/models.py:532 templates/includes/sidebar.html:57
msgid "Yearly"
msgstr ""
#: apps/transactions/models.py:529 apps/users/models.py:464
#: apps/transactions/models.py:533 apps/users/models.py:464
#: templates/includes/sidebar.html:51
msgid "Monthly"
msgstr ""
#: apps/transactions/models.py:530
#: apps/transactions/models.py:534
msgid "Weekly"
msgstr ""
#: apps/transactions/models.py:531
#: apps/transactions/models.py:535
msgid "Daily"
msgstr ""
#: apps/transactions/models.py:544
#: apps/transactions/models.py:548
msgid "Number of Installments"
msgstr ""
#: apps/transactions/models.py:549
#: apps/transactions/models.py:553
msgid "Installment Start"
msgstr ""
#: apps/transactions/models.py:550
#: apps/transactions/models.py:554
msgid "The installment number to start counting from"
msgstr ""
#: apps/transactions/models.py:555 apps/transactions/models.py:790
#: apps/transactions/models.py:559 apps/transactions/models.py:794
msgid "Start Date"
msgstr ""
#: apps/transactions/models.py:559 apps/transactions/models.py:791
#: apps/transactions/models.py:563 apps/transactions/models.py:795
msgid "End Date"
msgstr ""
#: apps/transactions/models.py:564
#: apps/transactions/models.py:568
msgid "Recurrence"
msgstr ""
#: apps/transactions/models.py:567
#: apps/transactions/models.py:571
msgid "Installment Amount"
msgstr ""
#: apps/transactions/models.py:586 apps/transactions/models.py:810
#: apps/transactions/models.py:590 apps/transactions/models.py:814
msgid "Add description to transactions"
msgstr ""
#: apps/transactions/models.py:589 apps/transactions/models.py:813
#: apps/transactions/models.py:593 apps/transactions/models.py:817
msgid "Add notes to transactions"
msgstr ""
#: apps/transactions/models.py:749
#: apps/transactions/models.py:753
msgid "day(s)"
msgstr ""
#: apps/transactions/models.py:750
#: apps/transactions/models.py:754
msgid "week(s)"
msgstr ""
#: apps/transactions/models.py:751
#: apps/transactions/models.py:755
msgid "month(s)"
msgstr ""
#: apps/transactions/models.py:752
#: apps/transactions/models.py:756
msgid "year(s)"
msgstr ""
#: apps/transactions/models.py:754
#: apps/transactions/models.py:758
#: templates/recurring_transactions/fragments/list.html:18
msgid "Paused"
msgstr ""
#: apps/transactions/models.py:793
#: apps/transactions/models.py:797
msgid "Recurrence Type"
msgstr ""
#: apps/transactions/models.py:796
#: apps/transactions/models.py:800
msgid "Recurrence Interval"
msgstr ""
#: apps/transactions/models.py:799
#: apps/transactions/models.py:803
msgid "Keep at most"
msgstr ""
#: apps/transactions/models.py:803
#: apps/transactions/models.py:807
msgid "Last Generated Date"
msgstr ""
#: apps/transactions/models.py:806
#: apps/transactions/models.py:810
msgid "Last Generated Reference Date"
msgstr ""
#: apps/transactions/models.py:1050
#: apps/transactions/models.py:1054
#: apps/transactions/views/quick_transactions.py:178
#: apps/transactions/views/quick_transactions.py:187
#: apps/transactions/views/quick_transactions.py:189
@@ -1591,7 +1621,7 @@ msgstr ""
msgid "Quick Transaction"
msgstr ""
#: apps/transactions/models.py:1051 templates/includes/sidebar.html:98
#: apps/transactions/models.py:1055 templates/includes/sidebar.html:98
#: templates/quick_transactions/pages/index.html:5
#: templates/quick_transactions/pages/index.html:15
msgid "Quick Transactions"
@@ -1697,7 +1727,7 @@ msgstr ""
#: apps/transactions/views/quick_transactions.py:156
#: apps/transactions/views/transactions.py:53
#: apps/transactions/views/transactions.py:228
#: apps/transactions/views/transactions.py:238
msgid "Transaction added successfully"
msgstr ""
@@ -1737,30 +1767,30 @@ msgstr ""
msgid "Tag deleted successfully"
msgstr ""
#: apps/transactions/views/transactions.py:252
#: apps/transactions/views/transactions.py:262
msgid "Transaction updated successfully"
msgstr ""
#: apps/transactions/views/transactions.py:303
#: apps/transactions/views/transactions.py:313
#, python-format
msgid "%(count)s transaction updated successfully"
msgid_plural "%(count)s transactions updated successfully"
msgstr[0] ""
msgstr[1] ""
#: apps/transactions/views/transactions.py:339
#: apps/transactions/views/transactions.py:349
msgid "Transaction duplicated successfully"
msgstr ""
#: apps/transactions/views/transactions.py:381
#: apps/transactions/views/transactions.py:391
msgid "Transaction deleted successfully"
msgstr ""
#: apps/transactions/views/transactions.py:399
#: apps/transactions/views/transactions.py:409
msgid "Transaction restored successfully"
msgstr ""
#: apps/transactions/views/transactions.py:425
#: apps/transactions/views/transactions.py:435
msgid "Transfer added successfully"
msgstr ""
@@ -1802,10 +1832,10 @@ msgid "This account is deactivated"
msgstr ""
#: apps/users/forms.py:62 apps/users/forms.py:75 apps/users/forms.py:97
#: templates/monthly_overview/pages/overview.html:95
#: templates/monthly_overview/pages/overview.html:141
#: templates/monthly_overview/pages/overview.html:98
#: templates/monthly_overview/pages/overview.html:245
#: templates/transactions/pages/transactions.html:47
#: templates/transactions/pages/transactions.html:94
#: templates/transactions/pages/transactions.html:195
msgid "Default"
msgstr ""
@@ -2225,10 +2255,6 @@ msgstr ""
msgid "Edit category"
msgstr ""
#: templates/categories/fragments/table.html:18
msgid "Muted"
msgstr ""
#: templates/categories/fragments/table.html:73
#: templates/insights/fragments/category_overview/index.html:552
msgid "No categories"
@@ -2247,9 +2273,9 @@ msgstr ""
#: templates/cotton/config/search.html:6
#: templates/import_app/fragments/profiles/list_presets.html:13
#: templates/monthly_overview/pages/overview.html:115
#: templates/monthly_overview/pages/overview.html:219
#: templates/rules/fragments/transaction_rule/dry_run/visual.html:57
#: templates/transactions/pages/transactions.html:67
#: templates/transactions/pages/transactions.html:168
msgid "Search"
msgstr ""
@@ -2477,8 +2503,8 @@ msgid "No entries for this DCA"
msgstr ""
#: templates/dca/fragments/strategy/details.html:120
#: templates/monthly_overview/fragments/list.html:33
#: templates/transactions/fragments/list_all.html:33
#: templates/monthly_overview/fragments/list.html:59
#: templates/transactions/fragments/list_all.html:59
msgid "Try adding one"
msgstr ""
@@ -2603,7 +2629,7 @@ msgstr ""
#: templates/exchange_rates/fragments/table.html:56
#: templates/exchange_rates_services/fragments/table.html:57
#: templates/transactions/fragments/list_all.html:43
#: templates/transactions/fragments/list_all.html:70
msgid "Page navigation"
msgstr ""
@@ -2623,15 +2649,22 @@ msgstr ""
msgid "Last fetch"
msgstr ""
#: templates/exchange_rates_services/fragments/list.html:61
#: templates/exchange_rates_services/fragments/list.html:62
#, python-format
msgid "%(counter)s consecutive failure"
msgid_plural "%(counter)s consecutive failures"
msgstr[0] ""
msgstr[1] ""
#: templates/exchange_rates_services/fragments/list.html:69
msgid "currencies"
msgstr ""
#: templates/exchange_rates_services/fragments/list.html:61
#: templates/exchange_rates_services/fragments/list.html:69
msgid "accounts"
msgstr ""
#: templates/exchange_rates_services/fragments/list.html:69
#: templates/exchange_rates_services/fragments/list.html:77
msgid "No services configured"
msgstr ""
@@ -2881,7 +2914,11 @@ msgid "Final total"
msgstr ""
#: templates/insights/fragments/category_overview/index.html:89
#: templates/monthly_overview/fragments/monthly_summary.html:165
#: templates/insights/fragments/month_by_month.html:91
#: templates/insights/fragments/month_by_month.html:186
#: templates/insights/fragments/year_by_year.html:59
#: templates/insights/fragments/year_by_year.html:140
#: templates/monthly_overview/fragments/monthly_summary.html:167
msgid "Total"
msgstr ""
@@ -2925,6 +2962,63 @@ msgstr ""
msgid "No recent transactions"
msgstr ""
#: templates/insights/fragments/month_by_month.html:86
#: templates/insights/fragments/year_by_year.html:54
msgid "Tag"
msgstr ""
#: templates/insights/fragments/month_by_month.html:94
msgid "Jan"
msgstr ""
#: templates/insights/fragments/month_by_month.html:95
msgid "Feb"
msgstr ""
#: templates/insights/fragments/month_by_month.html:96
msgid "Mar"
msgstr ""
#: templates/insights/fragments/month_by_month.html:97
msgid "Apr"
msgstr ""
#: templates/insights/fragments/month_by_month.html:98
msgid "May"
msgstr ""
#: templates/insights/fragments/month_by_month.html:99
msgid "Jun"
msgstr ""
#: templates/insights/fragments/month_by_month.html:100
msgid "Jul"
msgstr ""
#: templates/insights/fragments/month_by_month.html:101
msgid "Aug"
msgstr ""
#: templates/insights/fragments/month_by_month.html:102
msgid "Sep"
msgstr ""
#: templates/insights/fragments/month_by_month.html:103
msgid "Oct"
msgstr ""
#: templates/insights/fragments/month_by_month.html:104
msgid "Nov"
msgstr ""
#: templates/insights/fragments/month_by_month.html:105
msgid "Dec"
msgstr ""
#: templates/insights/fragments/month_by_month.html:248
msgid "No transactions for this year"
msgstr ""
#: templates/insights/fragments/sankey.html:100
msgid "From"
msgstr ""
@@ -2933,6 +3027,10 @@ msgstr ""
msgid "Percentage"
msgstr ""
#: templates/insights/fragments/year_by_year.html:202
msgid "No transactions"
msgstr ""
#: templates/insights/pages/index.html:37
msgid "Month"
msgstr ""
@@ -2982,6 +3080,14 @@ msgstr ""
msgid "Emergency Fund"
msgstr ""
#: templates/insights/pages/index.html:127
msgid "Year by Year"
msgstr ""
#: templates/insights/pages/index.html:132
msgid "Month by Month"
msgstr ""
#: templates/installment_plans/fragments/add.html:5
msgid "Add installment plan"
msgstr ""
@@ -3053,35 +3159,40 @@ msgstr ""
msgid "Item"
msgstr ""
#: templates/monthly_overview/fragments/list.html:32
#: templates/monthly_overview/fragments/list.html:15
#: templates/transactions/fragments/list_all.html:15
msgid "late"
msgstr ""
#: templates/monthly_overview/fragments/list.html:58
msgid "No transactions this month"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:6
#: templates/monthly_overview/fragments/monthly_summary.html:7
msgid "Daily Spending Allowance"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:6
#: templates/monthly_overview/fragments/monthly_summary.html:7
msgid "This is the final total divided by the remaining days in the month"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:42
#: templates/monthly_overview/fragments/monthly_summary.html:105
#: templates/monthly_overview/fragments/monthly_summary.html:168
#: templates/monthly_overview/fragments/monthly_summary.html:44
#: templates/monthly_overview/fragments/monthly_summary.html:107
#: templates/monthly_overview/fragments/monthly_summary.html:170
msgid "current"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:71
#: templates/monthly_overview/fragments/monthly_summary.html:134
#: templates/monthly_overview/fragments/monthly_summary.html:197
#: templates/monthly_overview/fragments/monthly_summary.html:73
#: templates/monthly_overview/fragments/monthly_summary.html:136
#: templates/monthly_overview/fragments/monthly_summary.html:199
msgid "projected"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:102
#: templates/monthly_overview/fragments/monthly_summary.html:104
msgid "Expenses"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:255
#: templates/monthly_overview/fragments/monthly_summary.html:257
msgid "Distribution"
msgstr ""
@@ -3089,27 +3200,27 @@ msgstr ""
msgid "Summary"
msgstr ""
#: templates/monthly_overview/pages/overview.html:96
#: templates/monthly_overview/pages/overview.html:150
#: templates/monthly_overview/pages/overview.html:99
#: templates/monthly_overview/pages/overview.html:254
#: templates/transactions/pages/transactions.html:48
#: templates/transactions/pages/transactions.html:103
#: templates/transactions/pages/transactions.html:204
msgid "Oldest first"
msgstr ""
#: templates/monthly_overview/pages/overview.html:97
#: templates/monthly_overview/pages/overview.html:159
#: templates/monthly_overview/pages/overview.html:100
#: templates/monthly_overview/pages/overview.html:263
#: templates/transactions/pages/transactions.html:49
#: templates/transactions/pages/transactions.html:112
#: templates/transactions/pages/transactions.html:213
msgid "Newest first"
msgstr ""
#: templates/monthly_overview/pages/overview.html:106
#: templates/monthly_overview/pages/overview.html:109
#: templates/transactions/pages/transactions.html:58
msgid "Filter transactions"
msgstr ""
#: templates/monthly_overview/pages/overview.html:131
#: templates/transactions/pages/transactions.html:84
#: templates/monthly_overview/pages/overview.html:235
#: templates/transactions/pages/transactions.html:185
msgid "Order by"
msgstr ""
@@ -3350,7 +3461,7 @@ msgstr ""
msgid "transactions"
msgstr ""
#: templates/transactions/fragments/list_all.html:32
#: templates/transactions/fragments/list_all.html:58
msgid "No transactions found"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-12-20 02:59+0000\n"
"POT-Creation-Date: 2026-01-10 20:50+0000\n"
"PO-Revision-Date: 2025-12-16 05:24+0000\n"
"Last-Translator: BRodolfo <simplysmartbydesign@gmail.com>\n"
"Language-Team: Spanish <https://translations.herculino.com/projects/wygiwyh/"
@@ -67,24 +67,30 @@ msgstr "Nuevo balance"
#: apps/transactions/forms.py:419 apps/transactions/forms.py:516
#: apps/transactions/forms.py:523 apps/transactions/forms.py:707
#: apps/transactions/forms.py:948 apps/transactions/models.py:322
#: apps/transactions/models.py:574 apps/transactions/models.py:774
#: apps/transactions/models.py:1022
#: apps/transactions/models.py:578 apps/transactions/models.py:778
#: apps/transactions/models.py:1026
#: templates/insights/fragments/category_overview/index.html:86
#: templates/insights/fragments/category_overview/index.html:542
#: templates/insights/fragments/month_by_month.html:84
#: templates/insights/fragments/year_by_year.html:52
msgid "Category"
msgstr "Categoría"
#: apps/accounts/forms.py:132 apps/dca/forms.py:95 apps/dca/forms.py:103
#: apps/export_app/forms.py:43 apps/export_app/forms.py:132
#: apps/rules/forms.py:184 apps/rules/forms.py:194 apps/rules/models.py:45
#: apps/rules/models.py:315 apps/transactions/filters.py:68
#: apps/rules/models.py:315 apps/transactions/filters.py:73
#: apps/transactions/forms.py:51 apps/transactions/forms.py:259
#: apps/transactions/forms.py:427 apps/transactions/forms.py:532
#: apps/transactions/forms.py:540 apps/transactions/forms.py:700
#: apps/transactions/forms.py:941 apps/transactions/models.py:328
#: apps/transactions/models.py:576 apps/transactions/models.py:778
#: apps/transactions/models.py:1028 templates/includes/sidebar.html:150
#: apps/transactions/models.py:580 apps/transactions/models.py:782
#: apps/transactions/models.py:1032 templates/includes/sidebar.html:150
#: templates/insights/fragments/category_overview/index.html:40
#: templates/insights/fragments/month_by_month.html:29
#: templates/insights/fragments/month_by_month.html:32
#: templates/insights/fragments/year_by_year.html:24
#: templates/insights/fragments/year_by_year.html:27
#: templates/tags/fragments/list.html:9 templates/tags/pages/index.html:4
msgid "Tags"
msgstr "Etiquetas"
@@ -92,7 +98,7 @@ msgstr "Etiquetas"
#: apps/accounts/models.py:12 apps/accounts/models.py:29 apps/dca/models.py:13
#: apps/import_app/models.py:14 apps/rules/models.py:13
#: apps/transactions/models.py:214 apps/transactions/models.py:239
#: apps/transactions/models.py:263 apps/transactions/models.py:990
#: apps/transactions/models.py:263 apps/transactions/models.py:994
#: templates/account_groups/fragments/list.html:22
#: templates/accounts/fragments/list.html:22
#: templates/categories/fragments/table.html:17
@@ -162,8 +168,8 @@ msgstr "Las cuentas archivadas no aparecen ni cuentan para su patrimonio neto"
#: apps/transactions/forms.py:63 apps/transactions/forms.py:271
#: apps/transactions/forms.py:386 apps/transactions/forms.py:692
#: apps/transactions/forms.py:933 apps/transactions/models.py:294
#: apps/transactions/models.py:534 apps/transactions/models.py:756
#: apps/transactions/models.py:996
#: apps/transactions/models.py:538 apps/transactions/models.py:760
#: apps/transactions/models.py:1000
#: templates/installment_plans/fragments/table.html:17
#: templates/quick_transactions/fragments/list.html:14
#: templates/recurring_transactions/fragments/table.html:19
@@ -172,11 +178,11 @@ msgid "Account"
msgstr "Cuenta"
#: apps/accounts/models.py:76 apps/export_app/forms.py:19
#: apps/export_app/forms.py:129 apps/transactions/filters.py:52
#: apps/export_app/forms.py:129 apps/transactions/filters.py:57
#: templates/accounts/fragments/list.html:9
#: templates/accounts/pages/index.html:4 templates/includes/sidebar.html:162
#: templates/includes/sidebar.html:164
#: templates/monthly_overview/pages/overview.html:75
#: templates/monthly_overview/pages/overview.html:77
#: templates/transactions/pages/transactions.html:26
msgid "Accounts"
msgstr "Cuentas"
@@ -193,8 +199,8 @@ msgstr "Grupo de Cuenta añadido exitosamente"
#: apps/accounts/views/account_groups.py:69
#: apps/accounts/views/account_groups.py:152 apps/accounts/views/accounts.py:68
#: apps/accounts/views/accounts.py:106 apps/dca/views.py:63
#: apps/dca/views.py:146 apps/rules/views.py:118 apps/rules/views.py:228
#: apps/accounts/views/accounts.py:106 apps/dca/views.py:62
#: apps/dca/views.py:145 apps/rules/views.py:118 apps/rules/views.py:228
#: apps/transactions/views/categories.py:91
#: apps/transactions/views/categories.py:129
#: apps/transactions/views/entities.py:91
@@ -208,7 +214,7 @@ msgid "Account Group updated successfully"
msgstr "Grupo de Cuenta actualizado exitosamente"
#: apps/accounts/views/account_groups.py:111
#: apps/accounts/views/accounts.py:145 apps/dca/views.py:105
#: apps/accounts/views/accounts.py:145 apps/dca/views.py:104
#: apps/rules/views.py:185 apps/transactions/views/categories.py:168
#: apps/transactions/views/entities.py:130 apps/transactions/views/tags.py:130
msgid "Item no longer shared with you"
@@ -219,14 +225,14 @@ msgid "Account Group deleted successfully"
msgstr "Grupo de Cuenta eliminado exitosamente"
#: apps/accounts/views/account_groups.py:135
#: apps/accounts/views/accounts.py:189 apps/dca/views.py:129
#: apps/accounts/views/accounts.py:189 apps/dca/views.py:128
#: apps/rules/views.py:210 apps/transactions/views/categories.py:192
#: apps/transactions/views/entities.py:154 apps/transactions/views/tags.py:154
msgid "Ownership taken successfully"
msgstr "Propiedad tomada exitosamete"
#: apps/accounts/views/account_groups.py:165
#: apps/accounts/views/accounts.py:119 apps/dca/views.py:159
#: apps/accounts/views/accounts.py:119 apps/dca/views.py:158
#: apps/rules/views.py:241 apps/transactions/views/categories.py:142
#: apps/transactions/views/entities.py:184 apps/transactions/views/tags.py:184
msgid "Configuration saved successfully"
@@ -364,7 +370,7 @@ msgid "Public"
msgstr "Público"
#: apps/common/templatetags/natural.py:20
#: templates/monthly_overview/fragments/monthly_summary.html:9
#: templates/monthly_overview/fragments/monthly_summary.html:10
msgid "today"
msgstr "hoy"
@@ -462,10 +468,10 @@ msgstr "Remover"
#: apps/common/widgets/tom_select.py:15
#: templates/mini_tools/unit_price_calculator.html:180
#: templates/monthly_overview/pages/overview.html:171
#: templates/monthly_overview/pages/overview.html:183
#: templates/transactions/pages/transactions.html:124
#: templates/transactions/pages/transactions.html:136
#: templates/monthly_overview/pages/overview.html:275
#: templates/monthly_overview/pages/overview.html:287
#: templates/transactions/pages/transactions.html:225
#: templates/transactions/pages/transactions.html:237
msgid "Clear"
msgstr "Limpiar"
@@ -504,11 +510,11 @@ msgid "Decimal Places"
msgstr "Cantidad de decimales"
#: apps/currencies/models.py:45 apps/export_app/forms.py:25
#: apps/export_app/forms.py:130 apps/transactions/filters.py:59
#: apps/export_app/forms.py:130 apps/transactions/filters.py:64
#: templates/currencies/fragments/list.html:9
#: templates/currencies/pages/index.html:4 templates/includes/sidebar.html:176
#: templates/includes/sidebar.html:178
#: templates/monthly_overview/pages/overview.html:62
#: templates/monthly_overview/pages/overview.html:63
#: templates/transactions/pages/transactions.html:12
msgid "Currencies"
msgstr "Monedas"
@@ -569,9 +575,9 @@ msgstr "Nombre del Servicio"
msgid "Service Type"
msgstr "Tipo de Servicio"
#: apps/currencies/models.py:118 apps/transactions/models.py:218
#: apps/transactions/models.py:242 apps/transactions/models.py:266
#: templates/categories/fragments/list.html:16
#: apps/currencies/models.py:118 apps/transactions/filters.py:27
#: apps/transactions/models.py:218 apps/transactions/models.py:242
#: apps/transactions/models.py:266 templates/categories/fragments/list.html:16
#: templates/entities/fragments/list.html:16
#: templates/installment_plans/fragments/list.html:16
#: templates/recurring_transactions/fragments/list.html:16
@@ -599,11 +605,11 @@ msgstr "Intervalo"
msgid "Last Successful Fetch"
msgstr "Última Sincronización Exitosa"
#: apps/currencies/models.py:141
#: apps/currencies/models.py:143
msgid "Target Currencies"
msgstr "Monedas de Destino"
#: apps/currencies/models.py:143
#: apps/currencies/models.py:145
msgid ""
"Select currencies to fetch exchange rates for. Rates will be fetched for "
"each currency against their set exchange currency."
@@ -612,11 +618,11 @@ msgstr ""
"tasas se consultarán para cada moneda en relación con su moneda de "
"referencia establecida."
#: apps/currencies/models.py:151
#: apps/currencies/models.py:153
msgid "Target Accounts"
msgstr "Cuentas de Destino"
#: apps/currencies/models.py:153
#: apps/currencies/models.py:155
msgid ""
"Select accounts to fetch exchange rates for. Rates will be fetched for each "
"account's currency against their set exchange currency."
@@ -625,33 +631,33 @@ msgstr ""
"tasas se consultarán para la moneda de cada cuenta en relación con su moneda "
"de referencia establecida."
#: apps/currencies/models.py:160
#: apps/currencies/models.py:162
msgid "Single exchange rate"
msgstr "Tasa de cambio única"
#: apps/currencies/models.py:163
#: apps/currencies/models.py:165
msgid "Create one exchange rate and keep updating it. Avoids database clutter."
msgstr ""
"Crea una única tasa de cambio y mantenla actualizada. Evita la acumulación "
"de datos en la base de datos."
#: apps/currencies/models.py:168
#: apps/currencies/models.py:170
msgid "Exchange Rate Service"
msgstr "Servicio de Tasas de Cambio"
#: apps/currencies/models.py:169
#: apps/currencies/models.py:171
msgid "Exchange Rate Services"
msgstr "Servicios de Tasas de Cambio"
#: apps/currencies/models.py:221
#: apps/currencies/models.py:223
msgid "'Every X hours' interval type requires a positive integer."
msgstr "El tipo de intervalo 'Cada X horas' requiere un entero positivo."
#: apps/currencies/models.py:230
#: apps/currencies/models.py:232
msgid "'Every X hours' interval must be between 1 and 24."
msgstr "El tipo de intervalo 'Cada X horas' debe ser ente 1 y 24."
#: apps/currencies/models.py:244
#: apps/currencies/models.py:246
msgid ""
"Invalid hour format. Use comma-separated hours (0-23) and/or ranges (e.g., "
"'1-5,8,10-12')."
@@ -659,7 +665,7 @@ msgstr ""
"Formato de hora no válido. Usa horas separadas por coma (0-23) y/o rangos "
"(p. ej., \"1-5,8,10-12\")."
#: apps/currencies/models.py:255
#: apps/currencies/models.py:257
msgid ""
"Invalid format. Please check the requirements for your selected interval "
"type."
@@ -760,8 +766,8 @@ msgstr "Moneda de pago"
#: apps/dca/models.py:26 apps/dca/models.py:181 apps/rules/forms.py:180
#: apps/rules/forms.py:196 apps/rules/models.py:43 apps/rules/models.py:295
#: apps/transactions/forms.py:413 apps/transactions/forms.py:560
#: apps/transactions/models.py:318 apps/transactions/models.py:583
#: apps/transactions/models.py:784 apps/transactions/models.py:1018
#: apps/transactions/models.py:318 apps/transactions/models.py:587
#: apps/transactions/models.py:788 apps/transactions/models.py:1022
msgid "Notes"
msgstr "Notas"
@@ -793,27 +799,27 @@ msgstr "Entrada DCA"
msgid "DCA Entries"
msgstr "Entradas DCA"
#: apps/dca/views.py:39
#: apps/dca/views.py:38
msgid "DCA Strategy added successfully"
msgstr "Estrategia DCA agregada con éxito"
#: apps/dca/views.py:76
#: apps/dca/views.py:75
msgid "DCA Strategy updated successfully"
msgstr "Estrategia DCA actualizada con éxito"
#: apps/dca/views.py:108
#: apps/dca/views.py:107
msgid "DCA strategy deleted successfully"
msgstr "Estrategia DCA eliminada con éxito"
#: apps/dca/views.py:238
#: apps/dca/views.py:237
msgid "Entry added successfully"
msgstr "Entrada agregada con éxito"
#: apps/dca/views.py:265
#: apps/dca/views.py:264
msgid "Entry updated successfully"
msgstr "Entrada actualizada con éxito"
#: apps/dca/views.py:291
#: apps/dca/views.py:290
msgid "Entry deleted successfully"
msgstr "Entrada eliminada con éxito"
@@ -833,34 +839,42 @@ msgid "Transactions"
msgstr "Transacciones"
#: apps/export_app/forms.py:37 apps/export_app/forms.py:131
#: apps/transactions/filters.py:63 templates/categories/fragments/list.html:9
#: apps/transactions/filters.py:68 templates/categories/fragments/list.html:9
#: templates/categories/pages/index.html:4 templates/includes/sidebar.html:144
#: templates/insights/fragments/month_by_month.html:18
#: templates/insights/fragments/month_by_month.html:21
#: templates/insights/fragments/year_by_year.html:13
#: templates/insights/fragments/year_by_year.html:16
msgid "Categories"
msgstr "Categorías"
#: apps/export_app/forms.py:49 apps/export_app/forms.py:133
#: apps/rules/forms.py:185 apps/rules/forms.py:195 apps/rules/models.py:46
#: apps/rules/models.py:307 apps/transactions/filters.py:73
#: apps/rules/models.py:307 apps/transactions/filters.py:78
#: apps/transactions/forms.py:59 apps/transactions/forms.py:267
#: apps/transactions/forms.py:435 apps/transactions/forms.py:715
#: apps/transactions/forms.py:956 apps/transactions/models.py:277
#: apps/transactions/models.py:333 apps/transactions/models.py:579
#: apps/transactions/models.py:781 apps/transactions/models.py:1033
#: apps/transactions/models.py:333 apps/transactions/models.py:583
#: apps/transactions/models.py:785 apps/transactions/models.py:1037
#: templates/entities/fragments/list.html:9
#: templates/entities/pages/index.html:4 templates/includes/sidebar.html:156
#: templates/insights/fragments/category_overview/index.html:54
#: templates/insights/fragments/month_by_month.html:40
#: templates/insights/fragments/month_by_month.html:43
#: templates/insights/fragments/year_by_year.html:35
#: templates/insights/fragments/year_by_year.html:38
msgid "Entities"
msgstr "Entidades"
#: apps/export_app/forms.py:55 apps/export_app/forms.py:137
#: apps/transactions/models.py:821 templates/includes/sidebar.html:110
#: apps/transactions/models.py:825 templates/includes/sidebar.html:110
#: templates/recurring_transactions/fragments/list.html:9
#: templates/recurring_transactions/pages/index.html:4
msgid "Recurring Transactions"
msgstr "Transacciones Recurrentes"
#: apps/export_app/forms.py:61 apps/export_app/forms.py:135
#: apps/transactions/models.py:597 templates/includes/sidebar.html:104
#: apps/transactions/models.py:601 templates/includes/sidebar.html:104
#: templates/installment_plans/fragments/list.html:9
#: templates/installment_plans/pages/index.html:4
msgid "Installment Plans"
@@ -1013,10 +1027,12 @@ msgid "Run deleted successfully"
msgstr "Tarea de importación eliminada con éxito"
#: apps/insights/forms.py:118 apps/insights/utils/sankey.py:36
#: apps/insights/utils/sankey.py:167 apps/transactions/filters.py:186
#: apps/insights/utils/sankey.py:167 apps/transactions/filters.py:203
#: templates/insights/fragments/category_overview/index.html:96
#: templates/insights/fragments/category_overview/index.html:407
#: templates/insights/fragments/category_overview/index.html:436
#: templates/insights/fragments/month_by_month.html:119
#: templates/insights/fragments/year_by_year.html:73
msgid "Uncategorized"
msgstr "Sin categoría"
@@ -1109,15 +1125,15 @@ msgstr "Operador"
#: apps/rules/forms.py:174 apps/rules/forms.py:188 apps/rules/models.py:36
#: apps/rules/models.py:271 apps/transactions/forms.py:377
#: apps/transactions/models.py:301 apps/transactions/models.py:539
#: apps/transactions/models.py:762 apps/transactions/models.py:1003
#: apps/transactions/models.py:301 apps/transactions/models.py:543
#: apps/transactions/models.py:766 apps/transactions/models.py:1007
msgid "Type"
msgstr "Tipo"
#: apps/rules/forms.py:175 apps/rules/forms.py:189 apps/rules/models.py:37
#: apps/rules/models.py:275 apps/transactions/filters.py:22
#: apps/transactions/forms.py:381 apps/transactions/models.py:303
#: apps/transactions/models.py:1005 templates/cotton/transaction/item.html:20
#: apps/transactions/models.py:1009 templates/cotton/transaction/item.html:20
#: templates/cotton/transaction/item.html:31
#: templates/transactions/widgets/paid_toggle_button.html:10
#: templates/transactions/widgets/unselectable_paid_toggle_button.html:13
@@ -1128,14 +1144,14 @@ msgstr "Pagado"
#: apps/rules/models.py:283 apps/transactions/forms.py:71
#: apps/transactions/forms.py:397 apps/transactions/forms.py:547
#: apps/transactions/forms.py:721 apps/transactions/models.py:305
#: apps/transactions/models.py:557 apps/transactions/models.py:786
#: apps/transactions/models.py:561 apps/transactions/models.py:790
msgid "Reference Date"
msgstr "Fecha de Referencia"
#: apps/rules/forms.py:178 apps/rules/forms.py:192 apps/rules/models.py:41
#: apps/rules/models.py:287 apps/transactions/forms.py:404
#: apps/transactions/models.py:311 apps/transactions/models.py:767
#: apps/transactions/models.py:1011
#: apps/transactions/models.py:311 apps/transactions/models.py:771
#: apps/transactions/models.py:1015
#: templates/insights/fragments/sankey.html:102
#: templates/installment_plans/fragments/table.html:18
#: templates/quick_transactions/fragments/list.html:15
@@ -1146,27 +1162,27 @@ msgstr "Monto"
#: apps/rules/forms.py:179 apps/rules/forms.py:193 apps/rules/models.py:14
#: apps/rules/models.py:42 apps/rules/models.py:291
#: apps/transactions/forms.py:408 apps/transactions/forms.py:551
#: apps/transactions/models.py:316 apps/transactions/models.py:541
#: apps/transactions/models.py:770 apps/transactions/models.py:1016
#: apps/transactions/models.py:316 apps/transactions/models.py:545
#: apps/transactions/models.py:774 apps/transactions/models.py:1020
msgid "Description"
msgstr "Descripción"
#: apps/rules/forms.py:182 apps/rules/forms.py:198 apps/rules/models.py:47
#: apps/rules/models.py:299 apps/transactions/models.py:355
#: apps/transactions/models.py:1038
#: apps/transactions/models.py:1042
msgid "Internal Note"
msgstr "Nota Interna"
#: apps/rules/forms.py:183 apps/rules/forms.py:199 apps/rules/models.py:48
#: apps/rules/models.py:303 apps/transactions/models.py:357
#: apps/transactions/models.py:1040
#: apps/transactions/models.py:1044
msgid "Internal ID"
msgstr "ID Interno"
#: apps/rules/forms.py:186 apps/rules/forms.py:200 apps/rules/models.py:40
#: apps/rules/models.py:319 apps/transactions/forms.py:564
#: apps/transactions/models.py:215 apps/transactions/models.py:306
#: apps/transactions/models.py:1006
#: apps/transactions/models.py:1010
msgid "Mute"
msgstr "Silenciar"
@@ -1324,53 +1340,67 @@ msgstr "Acción de Actualizar o Crear Transacción eliminada con éxito"
msgid "Projected"
msgstr "Proyectado"
#: apps/transactions/filters.py:40
#: apps/transactions/filters.py:28 templates/categories/fragments/table.html:18
msgid "Muted"
msgstr "Silenciado"
#: apps/transactions/filters.py:45
msgid "Content"
msgstr "Contenido"
#: apps/transactions/filters.py:46
#: apps/transactions/filters.py:51
msgid "Transaction Type"
msgstr "Tipo de Transacción"
#: apps/transactions/filters.py:84
#: apps/transactions/filters.py:89
#, fuzzy
#| msgid "Status"
msgid "Mute Status"
msgstr "Estado"
#: apps/transactions/filters.py:94
msgid "Date from"
msgstr "Desde la fecha"
#: apps/transactions/filters.py:89 apps/transactions/filters.py:99
#: apps/transactions/filters.py:99 apps/transactions/filters.py:109
msgid "Until"
msgstr "Hasta"
#: apps/transactions/filters.py:94
#: apps/transactions/filters.py:104
msgid "Reference date from"
msgstr "Desde la fecha de referencia"
#: apps/transactions/filters.py:104
#: apps/transactions/filters.py:114
msgid "Amount min"
msgstr "Monto mínimo"
#: apps/transactions/filters.py:109
#: apps/transactions/filters.py:119
msgid "Amount max"
msgstr "Monto máximo"
#: apps/transactions/filters.py:185
#: apps/transactions/filters.py:202
msgid "Categorized"
msgstr "Con Categoría"
#: apps/transactions/filters.py:192
#: apps/transactions/filters.py:209
msgid "Tagged"
msgstr "Etiquetado"
#: apps/transactions/filters.py:192
#: apps/transactions/filters.py:209
#: templates/insights/fragments/category_overview/index.html:189
#: templates/insights/fragments/month_by_month.html:121
#: templates/insights/fragments/year_by_year.html:75
msgid "Untagged"
msgstr "Sin etiqueta"
#: apps/transactions/filters.py:198
#: apps/transactions/filters.py:215
msgid "Any entity"
msgstr "Cualquier entidad"
#: apps/transactions/filters.py:199
#: apps/transactions/filters.py:216
#: templates/insights/fragments/category_overview/index.html:282
#: templates/insights/fragments/month_by_month.html:123
#: templates/insights/fragments/year_by_year.html:77
msgid "No entity"
msgstr "Ninguna entidad"
@@ -1466,10 +1496,12 @@ msgstr ""
"transacciones"
#: apps/transactions/models.py:276
#: templates/insights/fragments/month_by_month.html:88
#: templates/insights/fragments/year_by_year.html:56
msgid "Entity"
msgstr "Entidad"
#: apps/transactions/models.py:288 apps/transactions/models.py:983
#: apps/transactions/models.py:288 apps/transactions/models.py:987
#: templates/calendar_view/fragments/list.html:42
#: templates/calendar_view/fragments/list.html:44
#: templates/calendar_view/fragments/list.html:52
@@ -1477,11 +1509,11 @@ msgstr "Entidad"
#: templates/cotton/ui/quick_transactions_buttons.html:10
#: templates/cotton/ui/transactions_fab.html:10
#: templates/insights/fragments/category_overview/index.html:87
#: templates/monthly_overview/fragments/monthly_summary.html:39
#: templates/monthly_overview/fragments/monthly_summary.html:41
msgid "Income"
msgstr "Ingreso"
#: apps/transactions/models.py:289 apps/transactions/models.py:984
#: apps/transactions/models.py:289 apps/transactions/models.py:988
#: templates/calendar_view/fragments/list.html:46
#: templates/calendar_view/fragments/list.html:48
#: templates/calendar_view/fragments/list.html:56
@@ -1492,11 +1524,11 @@ msgstr "Ingreso"
msgid "Expense"
msgstr "Gasto"
#: apps/transactions/models.py:344 apps/transactions/models.py:596
#: apps/transactions/models.py:344 apps/transactions/models.py:600
msgid "Installment Plan"
msgstr "Plan de Cuotas"
#: apps/transactions/models.py:353 apps/transactions/models.py:820
#: apps/transactions/models.py:353 apps/transactions/models.py:824
msgid "Recurring Transaction"
msgstr "Transacción Recurrente"
@@ -1508,113 +1540,113 @@ msgstr "Eliminado"
msgid "Deleted At"
msgstr "Eliminado en"
#: apps/transactions/models.py:476 templates/tags/fragments/table.html:69
#: apps/transactions/models.py:480 templates/tags/fragments/table.html:69
msgid "No tags"
msgstr "Sin etiquetas"
#: apps/transactions/models.py:478
#: apps/transactions/models.py:482
msgid "No category"
msgstr "Sin categoría"
#: apps/transactions/models.py:480
#: apps/transactions/models.py:484
msgid "No description"
msgstr "Sin descripción"
#: apps/transactions/models.py:528 templates/includes/sidebar.html:57
#: apps/transactions/models.py:532 templates/includes/sidebar.html:57
msgid "Yearly"
msgstr "Anual"
#: apps/transactions/models.py:529 apps/users/models.py:464
#: apps/transactions/models.py:533 apps/users/models.py:464
#: templates/includes/sidebar.html:51
msgid "Monthly"
msgstr "Mensual"
#: apps/transactions/models.py:530
#: apps/transactions/models.py:534
msgid "Weekly"
msgstr "Semanal"
#: apps/transactions/models.py:531
#: apps/transactions/models.py:535
msgid "Daily"
msgstr "Diario"
#: apps/transactions/models.py:544
#: apps/transactions/models.py:548
msgid "Number of Installments"
msgstr "Cantidad de cuotas"
#: apps/transactions/models.py:549
#: apps/transactions/models.py:553
msgid "Installment Start"
msgstr "Cuota de Inicio"
#: apps/transactions/models.py:550
#: apps/transactions/models.py:554
msgid "The installment number to start counting from"
msgstr "El número de la cuota desde la cual comenzar a contar"
#: apps/transactions/models.py:555 apps/transactions/models.py:790
#: apps/transactions/models.py:559 apps/transactions/models.py:794
msgid "Start Date"
msgstr "Fecha de Inicio"
#: apps/transactions/models.py:559 apps/transactions/models.py:791
#: apps/transactions/models.py:563 apps/transactions/models.py:795
msgid "End Date"
msgstr "Fecha de Fin"
#: apps/transactions/models.py:564
#: apps/transactions/models.py:568
msgid "Recurrence"
msgstr "Recurrencia"
#: apps/transactions/models.py:567
#: apps/transactions/models.py:571
msgid "Installment Amount"
msgstr "Monto de la Cuota"
#: apps/transactions/models.py:586 apps/transactions/models.py:810
#: apps/transactions/models.py:590 apps/transactions/models.py:814
msgid "Add description to transactions"
msgstr "Agregar descripción a las transacciones"
#: apps/transactions/models.py:589 apps/transactions/models.py:813
#: apps/transactions/models.py:593 apps/transactions/models.py:817
msgid "Add notes to transactions"
msgstr "Agregar notas a las transacciones"
#: apps/transactions/models.py:749
#: apps/transactions/models.py:753
msgid "day(s)"
msgstr "día(s)"
#: apps/transactions/models.py:750
#: apps/transactions/models.py:754
msgid "week(s)"
msgstr "semana(s)"
#: apps/transactions/models.py:751
#: apps/transactions/models.py:755
msgid "month(s)"
msgstr "mes(es)"
#: apps/transactions/models.py:752
#: apps/transactions/models.py:756
msgid "year(s)"
msgstr "año(s)"
#: apps/transactions/models.py:754
#: apps/transactions/models.py:758
#: templates/recurring_transactions/fragments/list.html:18
msgid "Paused"
msgstr "Pausado"
#: apps/transactions/models.py:793
#: apps/transactions/models.py:797
msgid "Recurrence Type"
msgstr "Tipo de Recurrencia"
#: apps/transactions/models.py:796
#: apps/transactions/models.py:800
msgid "Recurrence Interval"
msgstr "Intervalo de Recurrencia"
#: apps/transactions/models.py:799
#: apps/transactions/models.py:803
msgid "Keep at most"
msgstr "Mantener como máximo"
#: apps/transactions/models.py:803
#: apps/transactions/models.py:807
msgid "Last Generated Date"
msgstr "Última Fecha Generada"
#: apps/transactions/models.py:806
#: apps/transactions/models.py:810
msgid "Last Generated Reference Date"
msgstr "Última Fecha de Referencia Generada"
#: apps/transactions/models.py:1050
#: apps/transactions/models.py:1054
#: apps/transactions/views/quick_transactions.py:178
#: apps/transactions/views/quick_transactions.py:187
#: apps/transactions/views/quick_transactions.py:189
@@ -1623,7 +1655,7 @@ msgstr "Última Fecha de Referencia Generada"
msgid "Quick Transaction"
msgstr "Transacción Rápida"
#: apps/transactions/models.py:1051 templates/includes/sidebar.html:98
#: apps/transactions/models.py:1055 templates/includes/sidebar.html:98
#: templates/quick_transactions/pages/index.html:5
#: templates/quick_transactions/pages/index.html:15
msgid "Quick Transactions"
@@ -1729,7 +1761,7 @@ msgstr "Ítem eliminado con éxito"
#: apps/transactions/views/quick_transactions.py:156
#: apps/transactions/views/transactions.py:53
#: apps/transactions/views/transactions.py:228
#: apps/transactions/views/transactions.py:238
msgid "Transaction added successfully"
msgstr "Transacción añadida exitosamente"
@@ -1769,30 +1801,30 @@ msgstr "Etiqueta actualizada con éxito"
msgid "Tag deleted successfully"
msgstr "Etiqueta eliminada con éxito"
#: apps/transactions/views/transactions.py:252
#: apps/transactions/views/transactions.py:262
msgid "Transaction updated successfully"
msgstr "Transacción actualizada con éxito"
#: apps/transactions/views/transactions.py:303
#: apps/transactions/views/transactions.py:313
#, python-format
msgid "%(count)s transaction updated successfully"
msgid_plural "%(count)s transactions updated successfully"
msgstr[0] "%(count)s transacción actualizada exitosamente"
msgstr[1] "%(count)s transacciones actualizadas exitosamente"
#: apps/transactions/views/transactions.py:339
#: apps/transactions/views/transactions.py:349
msgid "Transaction duplicated successfully"
msgstr "Transacción duplicada exitosamente"
#: apps/transactions/views/transactions.py:381
#: apps/transactions/views/transactions.py:391
msgid "Transaction deleted successfully"
msgstr "Transacción borrada exitosamente"
#: apps/transactions/views/transactions.py:399
#: apps/transactions/views/transactions.py:409
msgid "Transaction restored successfully"
msgstr "Transacción restaurada exitosamente"
#: apps/transactions/views/transactions.py:425
#: apps/transactions/views/transactions.py:435
msgid "Transfer added successfully"
msgstr "Transferencia añadida exitosamente"
@@ -1834,10 +1866,10 @@ msgid "This account is deactivated"
msgstr "Esta cuenta está desactivada"
#: apps/users/forms.py:62 apps/users/forms.py:75 apps/users/forms.py:97
#: templates/monthly_overview/pages/overview.html:95
#: templates/monthly_overview/pages/overview.html:141
#: templates/monthly_overview/pages/overview.html:98
#: templates/monthly_overview/pages/overview.html:245
#: templates/transactions/pages/transactions.html:47
#: templates/transactions/pages/transactions.html:94
#: templates/transactions/pages/transactions.html:195
msgid "Default"
msgstr "Por Defecto"
@@ -2265,10 +2297,6 @@ msgstr "Agregar categoría"
msgid "Edit category"
msgstr "Editar categoría"
#: templates/categories/fragments/table.html:18
msgid "Muted"
msgstr "Silenciado"
#: templates/categories/fragments/table.html:73
#: templates/insights/fragments/category_overview/index.html:552
msgid "No categories"
@@ -2287,9 +2315,9 @@ msgstr "Cerrar"
#: templates/cotton/config/search.html:6
#: templates/import_app/fragments/profiles/list_presets.html:13
#: templates/monthly_overview/pages/overview.html:115
#: templates/monthly_overview/pages/overview.html:219
#: templates/rules/fragments/transaction_rule/dry_run/visual.html:57
#: templates/transactions/pages/transactions.html:67
#: templates/transactions/pages/transactions.html:168
msgid "Search"
msgstr "Buscar"
@@ -2517,8 +2545,8 @@ msgid "No entries for this DCA"
msgstr "Sin entradas para este DCA"
#: templates/dca/fragments/strategy/details.html:120
#: templates/monthly_overview/fragments/list.html:33
#: templates/transactions/fragments/list_all.html:33
#: templates/monthly_overview/fragments/list.html:59
#: templates/transactions/fragments/list_all.html:59
msgid "Try adding one"
msgstr "Prueba agregar una"
@@ -2644,7 +2672,7 @@ msgstr "No hay tasas de cambio"
#: templates/exchange_rates/fragments/table.html:56
#: templates/exchange_rates_services/fragments/table.html:57
#: templates/transactions/fragments/list_all.html:43
#: templates/transactions/fragments/list_all.html:70
msgid "Page navigation"
msgstr "Navegación entre páginas"
@@ -2664,15 +2692,22 @@ msgstr "Dirigido a"
msgid "Last fetch"
msgstr "Última sincronización"
#: templates/exchange_rates_services/fragments/list.html:61
#: templates/exchange_rates_services/fragments/list.html:62
#, python-format
msgid "%(counter)s consecutive failure"
msgid_plural "%(counter)s consecutive failures"
msgstr[0] ""
msgstr[1] ""
#: templates/exchange_rates_services/fragments/list.html:69
msgid "currencies"
msgstr "monedas"
#: templates/exchange_rates_services/fragments/list.html:61
#: templates/exchange_rates_services/fragments/list.html:69
msgid "accounts"
msgstr "cuentas"
#: templates/exchange_rates_services/fragments/list.html:69
#: templates/exchange_rates_services/fragments/list.html:77
msgid "No services configured"
msgstr "No hay servicios configurados"
@@ -2927,7 +2962,11 @@ msgid "Final total"
msgstr "Total final"
#: templates/insights/fragments/category_overview/index.html:89
#: templates/monthly_overview/fragments/monthly_summary.html:165
#: templates/insights/fragments/month_by_month.html:91
#: templates/insights/fragments/month_by_month.html:186
#: templates/insights/fragments/year_by_year.html:59
#: templates/insights/fragments/year_by_year.html:140
#: templates/monthly_overview/fragments/monthly_summary.html:167
msgid "Total"
msgstr "Total"
@@ -2971,6 +3010,75 @@ msgstr "No hay transacciones atrasadas"
msgid "No recent transactions"
msgstr "No hay transacciones recientes"
#: templates/insights/fragments/month_by_month.html:86
#: templates/insights/fragments/year_by_year.html:54
#, fuzzy
#| msgid "Tags"
msgid "Tag"
msgstr "Etiquetas"
#: templates/insights/fragments/month_by_month.html:94
msgid "Jan"
msgstr ""
#: templates/insights/fragments/month_by_month.html:95
msgid "Feb"
msgstr ""
#: templates/insights/fragments/month_by_month.html:96
#, fuzzy
#| msgid "Max"
msgid "Mar"
msgstr "Máximo"
#: templates/insights/fragments/month_by_month.html:97
msgid "Apr"
msgstr ""
#: templates/insights/fragments/month_by_month.html:98
#, fuzzy
#| msgid "Max"
msgid "May"
msgstr "Máximo"
#: templates/insights/fragments/month_by_month.html:99
msgid "Jun"
msgstr ""
#: templates/insights/fragments/month_by_month.html:100
msgid "Jul"
msgstr ""
#: templates/insights/fragments/month_by_month.html:101
msgid "Aug"
msgstr ""
#: templates/insights/fragments/month_by_month.html:102
#, fuzzy
#| msgid "Set"
msgid "Sep"
msgstr "Establecer"
#: templates/insights/fragments/month_by_month.html:103
msgid "Oct"
msgstr ""
#: templates/insights/fragments/month_by_month.html:104
#, fuzzy
#| msgid "Now"
msgid "Nov"
msgstr "Ahora"
#: templates/insights/fragments/month_by_month.html:105
msgid "Dec"
msgstr ""
#: templates/insights/fragments/month_by_month.html:248
#, fuzzy
#| msgid "No transactions on this date"
msgid "No transactions for this year"
msgstr "No hay transacciones en esta fecha"
#: templates/insights/fragments/sankey.html:100
msgid "From"
msgstr "Desde"
@@ -2979,6 +3087,12 @@ msgstr "Desde"
msgid "Percentage"
msgstr "Porcentaje"
#: templates/insights/fragments/year_by_year.html:202
#, fuzzy
#| msgid "transactions"
msgid "No transactions"
msgstr "transacciones"
#: templates/insights/pages/index.html:37
msgid "Month"
msgstr "Mes"
@@ -3028,6 +3142,16 @@ msgstr "Últimas Transacciones"
msgid "Emergency Fund"
msgstr "Fondo de Emergencia"
#: templates/insights/pages/index.html:127
#, fuzzy
#| msgid "Yearly by account"
msgid "Year by Year"
msgstr "Anual por cuenta"
#: templates/insights/pages/index.html:132
msgid "Month by Month"
msgstr ""
#: templates/installment_plans/fragments/add.html:5
msgid "Add installment plan"
msgstr "Agregar plan de cuotas"
@@ -3101,35 +3225,40 @@ msgstr "Precio unitario"
msgid "Item"
msgstr "Ítem"
#: templates/monthly_overview/fragments/list.html:32
#: templates/monthly_overview/fragments/list.html:15
#: templates/transactions/fragments/list_all.html:15
msgid "late"
msgstr ""
#: templates/monthly_overview/fragments/list.html:58
msgid "No transactions this month"
msgstr "Sin transacciones este mes"
#: templates/monthly_overview/fragments/monthly_summary.html:6
#: templates/monthly_overview/fragments/monthly_summary.html:7
msgid "Daily Spending Allowance"
msgstr "Asignación de gastos diarios"
#: templates/monthly_overview/fragments/monthly_summary.html:6
#: templates/monthly_overview/fragments/monthly_summary.html:7
msgid "This is the final total divided by the remaining days in the month"
msgstr "Este es el total final dividido por los días restantes del mes"
#: templates/monthly_overview/fragments/monthly_summary.html:42
#: templates/monthly_overview/fragments/monthly_summary.html:105
#: templates/monthly_overview/fragments/monthly_summary.html:168
#: templates/monthly_overview/fragments/monthly_summary.html:44
#: templates/monthly_overview/fragments/monthly_summary.html:107
#: templates/monthly_overview/fragments/monthly_summary.html:170
msgid "current"
msgstr "actual"
#: templates/monthly_overview/fragments/monthly_summary.html:71
#: templates/monthly_overview/fragments/monthly_summary.html:134
#: templates/monthly_overview/fragments/monthly_summary.html:197
#: templates/monthly_overview/fragments/monthly_summary.html:73
#: templates/monthly_overview/fragments/monthly_summary.html:136
#: templates/monthly_overview/fragments/monthly_summary.html:199
msgid "projected"
msgstr "proyectado"
#: templates/monthly_overview/fragments/monthly_summary.html:102
#: templates/monthly_overview/fragments/monthly_summary.html:104
msgid "Expenses"
msgstr "Gastos"
#: templates/monthly_overview/fragments/monthly_summary.html:255
#: templates/monthly_overview/fragments/monthly_summary.html:257
msgid "Distribution"
msgstr "Distribución"
@@ -3137,27 +3266,27 @@ msgstr "Distribución"
msgid "Summary"
msgstr "Resumen"
#: templates/monthly_overview/pages/overview.html:96
#: templates/monthly_overview/pages/overview.html:150
#: templates/monthly_overview/pages/overview.html:99
#: templates/monthly_overview/pages/overview.html:254
#: templates/transactions/pages/transactions.html:48
#: templates/transactions/pages/transactions.html:103
#: templates/transactions/pages/transactions.html:204
msgid "Oldest first"
msgstr "Lo más antiguo primero"
#: templates/monthly_overview/pages/overview.html:97
#: templates/monthly_overview/pages/overview.html:159
#: templates/monthly_overview/pages/overview.html:100
#: templates/monthly_overview/pages/overview.html:263
#: templates/transactions/pages/transactions.html:49
#: templates/transactions/pages/transactions.html:112
#: templates/transactions/pages/transactions.html:213
msgid "Newest first"
msgstr "Lo más nuevo primero"
#: templates/monthly_overview/pages/overview.html:106
#: templates/monthly_overview/pages/overview.html:109
#: templates/transactions/pages/transactions.html:58
msgid "Filter transactions"
msgstr "Filtrar transacciones"
#: templates/monthly_overview/pages/overview.html:131
#: templates/transactions/pages/transactions.html:84
#: templates/monthly_overview/pages/overview.html:235
#: templates/transactions/pages/transactions.html:185
msgid "Order by"
msgstr "Ordenar por"
@@ -3402,7 +3531,7 @@ msgstr "Edición"
msgid "transactions"
msgstr "transacciones"
#: templates/transactions/fragments/list_all.html:32
#: templates/transactions/fragments/list_all.html:58
msgid "No transactions found"
msgstr "No se encontraron transacciones"

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-12-20 02:59+0000\n"
"POT-Creation-Date: 2026-01-10 20:50+0000\n"
"PO-Revision-Date: 2025-10-07 20:17+0000\n"
"Last-Translator: Erwan Colin <zephone@protonmail.com>\n"
"Language-Team: French <https://translations.herculino.com/projects/wygiwyh/"
@@ -67,24 +67,30 @@ msgstr "Nouveau solde"
#: apps/transactions/forms.py:419 apps/transactions/forms.py:516
#: apps/transactions/forms.py:523 apps/transactions/forms.py:707
#: apps/transactions/forms.py:948 apps/transactions/models.py:322
#: apps/transactions/models.py:574 apps/transactions/models.py:774
#: apps/transactions/models.py:1022
#: apps/transactions/models.py:578 apps/transactions/models.py:778
#: apps/transactions/models.py:1026
#: templates/insights/fragments/category_overview/index.html:86
#: templates/insights/fragments/category_overview/index.html:542
#: templates/insights/fragments/month_by_month.html:84
#: templates/insights/fragments/year_by_year.html:52
msgid "Category"
msgstr "Catégorie"
#: apps/accounts/forms.py:132 apps/dca/forms.py:95 apps/dca/forms.py:103
#: apps/export_app/forms.py:43 apps/export_app/forms.py:132
#: apps/rules/forms.py:184 apps/rules/forms.py:194 apps/rules/models.py:45
#: apps/rules/models.py:315 apps/transactions/filters.py:68
#: apps/rules/models.py:315 apps/transactions/filters.py:73
#: apps/transactions/forms.py:51 apps/transactions/forms.py:259
#: apps/transactions/forms.py:427 apps/transactions/forms.py:532
#: apps/transactions/forms.py:540 apps/transactions/forms.py:700
#: apps/transactions/forms.py:941 apps/transactions/models.py:328
#: apps/transactions/models.py:576 apps/transactions/models.py:778
#: apps/transactions/models.py:1028 templates/includes/sidebar.html:150
#: apps/transactions/models.py:580 apps/transactions/models.py:782
#: apps/transactions/models.py:1032 templates/includes/sidebar.html:150
#: templates/insights/fragments/category_overview/index.html:40
#: templates/insights/fragments/month_by_month.html:29
#: templates/insights/fragments/month_by_month.html:32
#: templates/insights/fragments/year_by_year.html:24
#: templates/insights/fragments/year_by_year.html:27
#: templates/tags/fragments/list.html:9 templates/tags/pages/index.html:4
msgid "Tags"
msgstr "Etiquettes"
@@ -92,7 +98,7 @@ msgstr "Etiquettes"
#: apps/accounts/models.py:12 apps/accounts/models.py:29 apps/dca/models.py:13
#: apps/import_app/models.py:14 apps/rules/models.py:13
#: apps/transactions/models.py:214 apps/transactions/models.py:239
#: apps/transactions/models.py:263 apps/transactions/models.py:990
#: apps/transactions/models.py:263 apps/transactions/models.py:994
#: templates/account_groups/fragments/list.html:22
#: templates/accounts/fragments/list.html:22
#: templates/categories/fragments/table.html:17
@@ -164,8 +170,8 @@ msgstr ""
#: apps/transactions/forms.py:63 apps/transactions/forms.py:271
#: apps/transactions/forms.py:386 apps/transactions/forms.py:692
#: apps/transactions/forms.py:933 apps/transactions/models.py:294
#: apps/transactions/models.py:534 apps/transactions/models.py:756
#: apps/transactions/models.py:996
#: apps/transactions/models.py:538 apps/transactions/models.py:760
#: apps/transactions/models.py:1000
#: templates/installment_plans/fragments/table.html:17
#: templates/quick_transactions/fragments/list.html:14
#: templates/recurring_transactions/fragments/table.html:19
@@ -174,11 +180,11 @@ msgid "Account"
msgstr "Compte"
#: apps/accounts/models.py:76 apps/export_app/forms.py:19
#: apps/export_app/forms.py:129 apps/transactions/filters.py:52
#: apps/export_app/forms.py:129 apps/transactions/filters.py:57
#: templates/accounts/fragments/list.html:9
#: templates/accounts/pages/index.html:4 templates/includes/sidebar.html:162
#: templates/includes/sidebar.html:164
#: templates/monthly_overview/pages/overview.html:75
#: templates/monthly_overview/pages/overview.html:77
#: templates/transactions/pages/transactions.html:26
msgid "Accounts"
msgstr "Comptes"
@@ -195,8 +201,8 @@ msgstr "Groupe de compte ajouté avec succès"
#: apps/accounts/views/account_groups.py:69
#: apps/accounts/views/account_groups.py:152 apps/accounts/views/accounts.py:68
#: apps/accounts/views/accounts.py:106 apps/dca/views.py:63
#: apps/dca/views.py:146 apps/rules/views.py:118 apps/rules/views.py:228
#: apps/accounts/views/accounts.py:106 apps/dca/views.py:62
#: apps/dca/views.py:145 apps/rules/views.py:118 apps/rules/views.py:228
#: apps/transactions/views/categories.py:91
#: apps/transactions/views/categories.py:129
#: apps/transactions/views/entities.py:91
@@ -210,7 +216,7 @@ msgid "Account Group updated successfully"
msgstr "Groupe de compte mis à jour avec succès"
#: apps/accounts/views/account_groups.py:111
#: apps/accounts/views/accounts.py:145 apps/dca/views.py:105
#: apps/accounts/views/accounts.py:145 apps/dca/views.py:104
#: apps/rules/views.py:185 apps/transactions/views/categories.py:168
#: apps/transactions/views/entities.py:130 apps/transactions/views/tags.py:130
msgid "Item no longer shared with you"
@@ -221,14 +227,14 @@ msgid "Account Group deleted successfully"
msgstr "Groupe de compte supprimé avec succès"
#: apps/accounts/views/account_groups.py:135
#: apps/accounts/views/accounts.py:189 apps/dca/views.py:129
#: apps/accounts/views/accounts.py:189 apps/dca/views.py:128
#: apps/rules/views.py:210 apps/transactions/views/categories.py:192
#: apps/transactions/views/entities.py:154 apps/transactions/views/tags.py:154
msgid "Ownership taken successfully"
msgstr "Propriété acquise avec succès"
#: apps/accounts/views/account_groups.py:165
#: apps/accounts/views/accounts.py:119 apps/dca/views.py:159
#: apps/accounts/views/accounts.py:119 apps/dca/views.py:158
#: apps/rules/views.py:241 apps/transactions/views/categories.py:142
#: apps/transactions/views/entities.py:184 apps/transactions/views/tags.py:184
msgid "Configuration saved successfully"
@@ -366,7 +372,7 @@ msgid "Public"
msgstr "Publique"
#: apps/common/templatetags/natural.py:20
#: templates/monthly_overview/fragments/monthly_summary.html:9
#: templates/monthly_overview/fragments/monthly_summary.html:10
msgid "today"
msgstr "aujourd'hui"
@@ -464,10 +470,10 @@ msgstr "Enlever"
#: apps/common/widgets/tom_select.py:15
#: templates/mini_tools/unit_price_calculator.html:180
#: templates/monthly_overview/pages/overview.html:171
#: templates/monthly_overview/pages/overview.html:183
#: templates/transactions/pages/transactions.html:124
#: templates/transactions/pages/transactions.html:136
#: templates/monthly_overview/pages/overview.html:275
#: templates/monthly_overview/pages/overview.html:287
#: templates/transactions/pages/transactions.html:225
#: templates/transactions/pages/transactions.html:237
msgid "Clear"
msgstr "Vider"
@@ -506,11 +512,11 @@ msgid "Decimal Places"
msgstr "Décimales"
#: apps/currencies/models.py:45 apps/export_app/forms.py:25
#: apps/export_app/forms.py:130 apps/transactions/filters.py:59
#: apps/export_app/forms.py:130 apps/transactions/filters.py:64
#: templates/currencies/fragments/list.html:9
#: templates/currencies/pages/index.html:4 templates/includes/sidebar.html:176
#: templates/includes/sidebar.html:178
#: templates/monthly_overview/pages/overview.html:62
#: templates/monthly_overview/pages/overview.html:63
#: templates/transactions/pages/transactions.html:12
msgid "Currencies"
msgstr "Devises"
@@ -571,9 +577,9 @@ msgstr "Nom du Service"
msgid "Service Type"
msgstr "Type de Service"
#: apps/currencies/models.py:118 apps/transactions/models.py:218
#: apps/transactions/models.py:242 apps/transactions/models.py:266
#: templates/categories/fragments/list.html:16
#: apps/currencies/models.py:118 apps/transactions/filters.py:27
#: apps/transactions/models.py:218 apps/transactions/models.py:242
#: apps/transactions/models.py:266 templates/categories/fragments/list.html:16
#: templates/entities/fragments/list.html:16
#: templates/installment_plans/fragments/list.html:16
#: templates/recurring_transactions/fragments/list.html:16
@@ -601,11 +607,11 @@ msgstr "Intervalle"
msgid "Last Successful Fetch"
msgstr "Dernière récupération avec succès"
#: apps/currencies/models.py:141
#: apps/currencies/models.py:143
msgid "Target Currencies"
msgstr "Devises cibles"
#: apps/currencies/models.py:143
#: apps/currencies/models.py:145
msgid ""
"Select currencies to fetch exchange rates for. Rates will be fetched for "
"each currency against their set exchange currency."
@@ -613,11 +619,11 @@ msgstr ""
"Sélectionnez les devises pour récupérer leur taux de changes. Les taux "
"seront récupérés pour chaque devises par rapport à leur devise d'échange."
#: apps/currencies/models.py:151
#: apps/currencies/models.py:153
msgid "Target Accounts"
msgstr "Comptes cibles"
#: apps/currencies/models.py:153
#: apps/currencies/models.py:155
msgid ""
"Select accounts to fetch exchange rates for. Rates will be fetched for each "
"account's currency against their set exchange currency."
@@ -625,33 +631,33 @@ msgstr ""
"Sélectionnez les comptes pour récupérer leur taux de change. Les taux seront "
"récupérés pour chaque compte par rapport à leur devise d'échange."
#: apps/currencies/models.py:160
#: apps/currencies/models.py:162
msgid "Single exchange rate"
msgstr "Taux de change unique"
#: apps/currencies/models.py:163
#: apps/currencies/models.py:165
msgid "Create one exchange rate and keep updating it. Avoids database clutter."
msgstr ""
"Ne créer qu'un seul taux de change et le mettre à jour. Evite d'engorger la "
"base de donnée."
#: apps/currencies/models.py:168
#: apps/currencies/models.py:170
msgid "Exchange Rate Service"
msgstr "Service de taux de change"
#: apps/currencies/models.py:169
#: apps/currencies/models.py:171
msgid "Exchange Rate Services"
msgstr "Services de taux de change"
#: apps/currencies/models.py:221
#: apps/currencies/models.py:223
msgid "'Every X hours' interval type requires a positive integer."
msgstr "'Toutes les X heures' l'intervalle requiert un entier positif."
#: apps/currencies/models.py:230
#: apps/currencies/models.py:232
msgid "'Every X hours' interval must be between 1 and 24."
msgstr "'Toutes les X heures' l'intervalle doit être compris entre 1 et 24."
#: apps/currencies/models.py:244
#: apps/currencies/models.py:246
msgid ""
"Invalid hour format. Use comma-separated hours (0-23) and/or ranges (e.g., "
"'1-5,8,10-12')."
@@ -659,7 +665,7 @@ msgstr ""
"Format d'heure invalide. Utilisez les heures séparé par virgule (0-23) et/ou "
"une plage (ex : '1-5,8,10-12')."
#: apps/currencies/models.py:255
#: apps/currencies/models.py:257
msgid ""
"Invalid format. Please check the requirements for your selected interval "
"type."
@@ -761,8 +767,8 @@ msgstr "Devise de paiement"
#: apps/dca/models.py:26 apps/dca/models.py:181 apps/rules/forms.py:180
#: apps/rules/forms.py:196 apps/rules/models.py:43 apps/rules/models.py:295
#: apps/transactions/forms.py:413 apps/transactions/forms.py:560
#: apps/transactions/models.py:318 apps/transactions/models.py:583
#: apps/transactions/models.py:784 apps/transactions/models.py:1018
#: apps/transactions/models.py:318 apps/transactions/models.py:587
#: apps/transactions/models.py:788 apps/transactions/models.py:1022
msgid "Notes"
msgstr "Notes"
@@ -794,27 +800,27 @@ msgstr "Entrée DCA"
msgid "DCA Entries"
msgstr "Entrées DCA"
#: apps/dca/views.py:39
#: apps/dca/views.py:38
msgid "DCA Strategy added successfully"
msgstr "Stratégie DCA ajouté avec succès"
#: apps/dca/views.py:76
#: apps/dca/views.py:75
msgid "DCA Strategy updated successfully"
msgstr "Stratégie DCA mise à jour avec succès"
#: apps/dca/views.py:108
#: apps/dca/views.py:107
msgid "DCA strategy deleted successfully"
msgstr "Stratégie DCA supprimé avec succès"
#: apps/dca/views.py:238
#: apps/dca/views.py:237
msgid "Entry added successfully"
msgstr "Entrée ajoutée avec succès"
#: apps/dca/views.py:265
#: apps/dca/views.py:264
msgid "Entry updated successfully"
msgstr "Entrée mise à jour avec succès"
#: apps/dca/views.py:291
#: apps/dca/views.py:290
msgid "Entry deleted successfully"
msgstr "Entrée supprimée avec succès"
@@ -834,34 +840,42 @@ msgid "Transactions"
msgstr "Transactions"
#: apps/export_app/forms.py:37 apps/export_app/forms.py:131
#: apps/transactions/filters.py:63 templates/categories/fragments/list.html:9
#: apps/transactions/filters.py:68 templates/categories/fragments/list.html:9
#: templates/categories/pages/index.html:4 templates/includes/sidebar.html:144
#: templates/insights/fragments/month_by_month.html:18
#: templates/insights/fragments/month_by_month.html:21
#: templates/insights/fragments/year_by_year.html:13
#: templates/insights/fragments/year_by_year.html:16
msgid "Categories"
msgstr "Catégories"
#: apps/export_app/forms.py:49 apps/export_app/forms.py:133
#: apps/rules/forms.py:185 apps/rules/forms.py:195 apps/rules/models.py:46
#: apps/rules/models.py:307 apps/transactions/filters.py:73
#: apps/rules/models.py:307 apps/transactions/filters.py:78
#: apps/transactions/forms.py:59 apps/transactions/forms.py:267
#: apps/transactions/forms.py:435 apps/transactions/forms.py:715
#: apps/transactions/forms.py:956 apps/transactions/models.py:277
#: apps/transactions/models.py:333 apps/transactions/models.py:579
#: apps/transactions/models.py:781 apps/transactions/models.py:1033
#: apps/transactions/models.py:333 apps/transactions/models.py:583
#: apps/transactions/models.py:785 apps/transactions/models.py:1037
#: templates/entities/fragments/list.html:9
#: templates/entities/pages/index.html:4 templates/includes/sidebar.html:156
#: templates/insights/fragments/category_overview/index.html:54
#: templates/insights/fragments/month_by_month.html:40
#: templates/insights/fragments/month_by_month.html:43
#: templates/insights/fragments/year_by_year.html:35
#: templates/insights/fragments/year_by_year.html:38
msgid "Entities"
msgstr "Entités"
#: apps/export_app/forms.py:55 apps/export_app/forms.py:137
#: apps/transactions/models.py:821 templates/includes/sidebar.html:110
#: apps/transactions/models.py:825 templates/includes/sidebar.html:110
#: templates/recurring_transactions/fragments/list.html:9
#: templates/recurring_transactions/pages/index.html:4
msgid "Recurring Transactions"
msgstr "Transactions récurrentes"
#: apps/export_app/forms.py:61 apps/export_app/forms.py:135
#: apps/transactions/models.py:597 templates/includes/sidebar.html:104
#: apps/transactions/models.py:601 templates/includes/sidebar.html:104
#: templates/installment_plans/fragments/list.html:9
#: templates/installment_plans/pages/index.html:4
msgid "Installment Plans"
@@ -1015,10 +1029,12 @@ msgid "Run deleted successfully"
msgstr "Exécution supprimé avec succès"
#: apps/insights/forms.py:118 apps/insights/utils/sankey.py:36
#: apps/insights/utils/sankey.py:167 apps/transactions/filters.py:186
#: apps/insights/utils/sankey.py:167 apps/transactions/filters.py:203
#: templates/insights/fragments/category_overview/index.html:96
#: templates/insights/fragments/category_overview/index.html:407
#: templates/insights/fragments/category_overview/index.html:436
#: templates/insights/fragments/month_by_month.html:119
#: templates/insights/fragments/year_by_year.html:73
msgid "Uncategorized"
msgstr "Sans catégorie"
@@ -1111,15 +1127,15 @@ msgstr "Opérateur"
#: apps/rules/forms.py:174 apps/rules/forms.py:188 apps/rules/models.py:36
#: apps/rules/models.py:271 apps/transactions/forms.py:377
#: apps/transactions/models.py:301 apps/transactions/models.py:539
#: apps/transactions/models.py:762 apps/transactions/models.py:1003
#: apps/transactions/models.py:301 apps/transactions/models.py:543
#: apps/transactions/models.py:766 apps/transactions/models.py:1007
msgid "Type"
msgstr "Type"
#: apps/rules/forms.py:175 apps/rules/forms.py:189 apps/rules/models.py:37
#: apps/rules/models.py:275 apps/transactions/filters.py:22
#: apps/transactions/forms.py:381 apps/transactions/models.py:303
#: apps/transactions/models.py:1005 templates/cotton/transaction/item.html:20
#: apps/transactions/models.py:1009 templates/cotton/transaction/item.html:20
#: templates/cotton/transaction/item.html:31
#: templates/transactions/widgets/paid_toggle_button.html:10
#: templates/transactions/widgets/unselectable_paid_toggle_button.html:13
@@ -1130,14 +1146,14 @@ msgstr "Payé"
#: apps/rules/models.py:283 apps/transactions/forms.py:71
#: apps/transactions/forms.py:397 apps/transactions/forms.py:547
#: apps/transactions/forms.py:721 apps/transactions/models.py:305
#: apps/transactions/models.py:557 apps/transactions/models.py:786
#: apps/transactions/models.py:561 apps/transactions/models.py:790
msgid "Reference Date"
msgstr "Date de référence"
#: apps/rules/forms.py:178 apps/rules/forms.py:192 apps/rules/models.py:41
#: apps/rules/models.py:287 apps/transactions/forms.py:404
#: apps/transactions/models.py:311 apps/transactions/models.py:767
#: apps/transactions/models.py:1011
#: apps/transactions/models.py:311 apps/transactions/models.py:771
#: apps/transactions/models.py:1015
#: templates/insights/fragments/sankey.html:102
#: templates/installment_plans/fragments/table.html:18
#: templates/quick_transactions/fragments/list.html:15
@@ -1148,27 +1164,27 @@ msgstr "Montant"
#: apps/rules/forms.py:179 apps/rules/forms.py:193 apps/rules/models.py:14
#: apps/rules/models.py:42 apps/rules/models.py:291
#: apps/transactions/forms.py:408 apps/transactions/forms.py:551
#: apps/transactions/models.py:316 apps/transactions/models.py:541
#: apps/transactions/models.py:770 apps/transactions/models.py:1016
#: apps/transactions/models.py:316 apps/transactions/models.py:545
#: apps/transactions/models.py:774 apps/transactions/models.py:1020
msgid "Description"
msgstr "Description"
#: apps/rules/forms.py:182 apps/rules/forms.py:198 apps/rules/models.py:47
#: apps/rules/models.py:299 apps/transactions/models.py:355
#: apps/transactions/models.py:1038
#: apps/transactions/models.py:1042
msgid "Internal Note"
msgstr "Note interne"
#: apps/rules/forms.py:183 apps/rules/forms.py:199 apps/rules/models.py:48
#: apps/rules/models.py:303 apps/transactions/models.py:357
#: apps/transactions/models.py:1040
#: apps/transactions/models.py:1044
msgid "Internal ID"
msgstr "ID interne"
#: apps/rules/forms.py:186 apps/rules/forms.py:200 apps/rules/models.py:40
#: apps/rules/models.py:319 apps/transactions/forms.py:564
#: apps/transactions/models.py:215 apps/transactions/models.py:306
#: apps/transactions/models.py:1006
#: apps/transactions/models.py:1010
msgid "Mute"
msgstr "Silencieux"
@@ -1328,53 +1344,67 @@ msgstr ""
msgid "Projected"
msgstr "Projeté"
#: apps/transactions/filters.py:40
#: apps/transactions/filters.py:28 templates/categories/fragments/table.html:18
msgid "Muted"
msgstr "Muet"
#: apps/transactions/filters.py:45
msgid "Content"
msgstr "Contenu"
#: apps/transactions/filters.py:46
#: apps/transactions/filters.py:51
msgid "Transaction Type"
msgstr "Type de transaction"
#: apps/transactions/filters.py:84
#: apps/transactions/filters.py:89
#, fuzzy
#| msgid "Status"
msgid "Mute Status"
msgstr "Statut"
#: apps/transactions/filters.py:94
msgid "Date from"
msgstr "Date de départ"
#: apps/transactions/filters.py:89 apps/transactions/filters.py:99
#: apps/transactions/filters.py:99 apps/transactions/filters.py:109
msgid "Until"
msgstr "Jusqu'au"
#: apps/transactions/filters.py:94
#: apps/transactions/filters.py:104
msgid "Reference date from"
msgstr "Référencer une date de"
#: apps/transactions/filters.py:104
#: apps/transactions/filters.py:114
msgid "Amount min"
msgstr "Montant min"
#: apps/transactions/filters.py:109
#: apps/transactions/filters.py:119
msgid "Amount max"
msgstr "Montant max"
#: apps/transactions/filters.py:185
#: apps/transactions/filters.py:202
msgid "Categorized"
msgstr "Catégorisé"
#: apps/transactions/filters.py:192
#: apps/transactions/filters.py:209
msgid "Tagged"
msgstr "Avec étiquettes"
#: apps/transactions/filters.py:192
#: apps/transactions/filters.py:209
#: templates/insights/fragments/category_overview/index.html:189
#: templates/insights/fragments/month_by_month.html:121
#: templates/insights/fragments/year_by_year.html:75
msgid "Untagged"
msgstr "Sans étiquettes"
#: apps/transactions/filters.py:198
#: apps/transactions/filters.py:215
msgid "Any entity"
msgstr "N'importe quelle entité"
#: apps/transactions/filters.py:199
#: apps/transactions/filters.py:216
#: templates/insights/fragments/category_overview/index.html:282
#: templates/insights/fragments/month_by_month.html:123
#: templates/insights/fragments/year_by_year.html:77
msgid "No entity"
msgstr "Pas d'entité"
@@ -1470,10 +1500,12 @@ msgstr ""
"créations de nouvelles transactions"
#: apps/transactions/models.py:276
#: templates/insights/fragments/month_by_month.html:88
#: templates/insights/fragments/year_by_year.html:56
msgid "Entity"
msgstr "Entité"
#: apps/transactions/models.py:288 apps/transactions/models.py:983
#: apps/transactions/models.py:288 apps/transactions/models.py:987
#: templates/calendar_view/fragments/list.html:42
#: templates/calendar_view/fragments/list.html:44
#: templates/calendar_view/fragments/list.html:52
@@ -1481,11 +1513,11 @@ msgstr "Entité"
#: templates/cotton/ui/quick_transactions_buttons.html:10
#: templates/cotton/ui/transactions_fab.html:10
#: templates/insights/fragments/category_overview/index.html:87
#: templates/monthly_overview/fragments/monthly_summary.html:39
#: templates/monthly_overview/fragments/monthly_summary.html:41
msgid "Income"
msgstr "Revenus"
#: apps/transactions/models.py:289 apps/transactions/models.py:984
#: apps/transactions/models.py:289 apps/transactions/models.py:988
#: templates/calendar_view/fragments/list.html:46
#: templates/calendar_view/fragments/list.html:48
#: templates/calendar_view/fragments/list.html:56
@@ -1496,11 +1528,11 @@ msgstr "Revenus"
msgid "Expense"
msgstr "Dépense"
#: apps/transactions/models.py:344 apps/transactions/models.py:596
#: apps/transactions/models.py:344 apps/transactions/models.py:600
msgid "Installment Plan"
msgstr "Plan d'aménagement"
#: apps/transactions/models.py:353 apps/transactions/models.py:820
#: apps/transactions/models.py:353 apps/transactions/models.py:824
msgid "Recurring Transaction"
msgstr "Transaction récurrente"
@@ -1512,113 +1544,113 @@ msgstr "Supprimé"
msgid "Deleted At"
msgstr "Supprimé à"
#: apps/transactions/models.py:476 templates/tags/fragments/table.html:69
#: apps/transactions/models.py:480 templates/tags/fragments/table.html:69
msgid "No tags"
msgstr "Aucunes étiquettes"
#: apps/transactions/models.py:478
#: apps/transactions/models.py:482
msgid "No category"
msgstr "Pas de catégorie"
#: apps/transactions/models.py:480
#: apps/transactions/models.py:484
msgid "No description"
msgstr "Pas de description"
#: apps/transactions/models.py:528 templates/includes/sidebar.html:57
#: apps/transactions/models.py:532 templates/includes/sidebar.html:57
msgid "Yearly"
msgstr "Annuel"
#: apps/transactions/models.py:529 apps/users/models.py:464
#: apps/transactions/models.py:533 apps/users/models.py:464
#: templates/includes/sidebar.html:51
msgid "Monthly"
msgstr "Mensuel"
#: apps/transactions/models.py:530
#: apps/transactions/models.py:534
msgid "Weekly"
msgstr "Hebdomadaire"
#: apps/transactions/models.py:531
#: apps/transactions/models.py:535
msgid "Daily"
msgstr "Quotidien"
#: apps/transactions/models.py:544
#: apps/transactions/models.py:548
msgid "Number of Installments"
msgstr "Nombre d'écheances"
#: apps/transactions/models.py:549
#: apps/transactions/models.py:553
msgid "Installment Start"
msgstr "Commencer à l'échéance"
#: apps/transactions/models.py:550
#: apps/transactions/models.py:554
msgid "The installment number to start counting from"
msgstr "L'échéance à partir de laquelle compter"
#: apps/transactions/models.py:555 apps/transactions/models.py:790
#: apps/transactions/models.py:559 apps/transactions/models.py:794
msgid "Start Date"
msgstr "Date de début"
#: apps/transactions/models.py:559 apps/transactions/models.py:791
#: apps/transactions/models.py:563 apps/transactions/models.py:795
msgid "End Date"
msgstr "Date de fin"
#: apps/transactions/models.py:564
#: apps/transactions/models.py:568
msgid "Recurrence"
msgstr "Récurrence"
#: apps/transactions/models.py:567
#: apps/transactions/models.py:571
msgid "Installment Amount"
msgstr "Montant des échéances"
#: apps/transactions/models.py:586 apps/transactions/models.py:810
#: apps/transactions/models.py:590 apps/transactions/models.py:814
msgid "Add description to transactions"
msgstr "Rajouter une description à la transaction"
#: apps/transactions/models.py:589 apps/transactions/models.py:813
#: apps/transactions/models.py:593 apps/transactions/models.py:817
msgid "Add notes to transactions"
msgstr "Ajouter des notes aux transactions"
#: apps/transactions/models.py:749
#: apps/transactions/models.py:753
msgid "day(s)"
msgstr "jour(s)"
#: apps/transactions/models.py:750
#: apps/transactions/models.py:754
msgid "week(s)"
msgstr "semaine(s)"
#: apps/transactions/models.py:751
#: apps/transactions/models.py:755
msgid "month(s)"
msgstr "mois"
#: apps/transactions/models.py:752
#: apps/transactions/models.py:756
msgid "year(s)"
msgstr "année(s)"
#: apps/transactions/models.py:754
#: apps/transactions/models.py:758
#: templates/recurring_transactions/fragments/list.html:18
msgid "Paused"
msgstr "Interrompu"
#: apps/transactions/models.py:793
#: apps/transactions/models.py:797
msgid "Recurrence Type"
msgstr "Type de récurrence"
#: apps/transactions/models.py:796
#: apps/transactions/models.py:800
msgid "Recurrence Interval"
msgstr "Interval de récurrence"
#: apps/transactions/models.py:799
#: apps/transactions/models.py:803
msgid "Keep at most"
msgstr "Répéter un maximum de"
#: apps/transactions/models.py:803
#: apps/transactions/models.py:807
msgid "Last Generated Date"
msgstr "Dernière date générée"
#: apps/transactions/models.py:806
#: apps/transactions/models.py:810
msgid "Last Generated Reference Date"
msgstr "Dernière date de référence générée"
#: apps/transactions/models.py:1050
#: apps/transactions/models.py:1054
#: apps/transactions/views/quick_transactions.py:178
#: apps/transactions/views/quick_transactions.py:187
#: apps/transactions/views/quick_transactions.py:189
@@ -1627,7 +1659,7 @@ msgstr "Dernière date de référence générée"
msgid "Quick Transaction"
msgstr "Transaction rapide"
#: apps/transactions/models.py:1051 templates/includes/sidebar.html:98
#: apps/transactions/models.py:1055 templates/includes/sidebar.html:98
#: templates/quick_transactions/pages/index.html:5
#: templates/quick_transactions/pages/index.html:15
msgid "Quick Transactions"
@@ -1733,7 +1765,7 @@ msgstr "Item supprimée avec succès"
#: apps/transactions/views/quick_transactions.py:156
#: apps/transactions/views/transactions.py:53
#: apps/transactions/views/transactions.py:228
#: apps/transactions/views/transactions.py:238
msgid "Transaction added successfully"
msgstr "Transaction ajoutée avec succès"
@@ -1773,30 +1805,30 @@ msgstr "Etiquette mise à jour avec succès"
msgid "Tag deleted successfully"
msgstr "Etiquette supprimée avec succès"
#: apps/transactions/views/transactions.py:252
#: apps/transactions/views/transactions.py:262
msgid "Transaction updated successfully"
msgstr "Transaction mise à jour avec succès"
#: apps/transactions/views/transactions.py:303
#: apps/transactions/views/transactions.py:313
#, python-format
msgid "%(count)s transaction updated successfully"
msgid_plural "%(count)s transactions updated successfully"
msgstr[0] "%(count)s transaction mise à jour avec succès"
msgstr[1] "%(count)s transactions mises à jour avec succès"
#: apps/transactions/views/transactions.py:339
#: apps/transactions/views/transactions.py:349
msgid "Transaction duplicated successfully"
msgstr "Transaction dupliquée avec succès"
#: apps/transactions/views/transactions.py:381
#: apps/transactions/views/transactions.py:391
msgid "Transaction deleted successfully"
msgstr "Transaction supprimée avec succès"
#: apps/transactions/views/transactions.py:399
#: apps/transactions/views/transactions.py:409
msgid "Transaction restored successfully"
msgstr "Transaction restaurée avec succès"
#: apps/transactions/views/transactions.py:425
#: apps/transactions/views/transactions.py:435
msgid "Transfer added successfully"
msgstr "Virement ajouté avec succès"
@@ -1838,10 +1870,10 @@ msgid "This account is deactivated"
msgstr "Ce compte est désactivé"
#: apps/users/forms.py:62 apps/users/forms.py:75 apps/users/forms.py:97
#: templates/monthly_overview/pages/overview.html:95
#: templates/monthly_overview/pages/overview.html:141
#: templates/monthly_overview/pages/overview.html:98
#: templates/monthly_overview/pages/overview.html:245
#: templates/transactions/pages/transactions.html:47
#: templates/transactions/pages/transactions.html:94
#: templates/transactions/pages/transactions.html:195
msgid "Default"
msgstr "Par défaut"
@@ -2274,10 +2306,6 @@ msgstr "Ajouter une catégorie"
msgid "Edit category"
msgstr "Modifier la catégorie"
#: templates/categories/fragments/table.html:18
msgid "Muted"
msgstr "Muet"
#: templates/categories/fragments/table.html:73
#: templates/insights/fragments/category_overview/index.html:552
msgid "No categories"
@@ -2296,9 +2324,9 @@ msgstr "Fermer"
#: templates/cotton/config/search.html:6
#: templates/import_app/fragments/profiles/list_presets.html:13
#: templates/monthly_overview/pages/overview.html:115
#: templates/monthly_overview/pages/overview.html:219
#: templates/rules/fragments/transaction_rule/dry_run/visual.html:57
#: templates/transactions/pages/transactions.html:67
#: templates/transactions/pages/transactions.html:168
msgid "Search"
msgstr "Rechercher"
@@ -2532,8 +2560,8 @@ msgid "No entries for this DCA"
msgstr "Pas d'entrées pour ce DCA"
#: templates/dca/fragments/strategy/details.html:120
#: templates/monthly_overview/fragments/list.html:33
#: templates/transactions/fragments/list_all.html:33
#: templates/monthly_overview/fragments/list.html:59
#: templates/transactions/fragments/list_all.html:59
msgid "Try adding one"
msgstr "Essayer d'en ajouter une"
@@ -2659,7 +2687,7 @@ msgstr "Pas de taux de change"
#: templates/exchange_rates/fragments/table.html:56
#: templates/exchange_rates_services/fragments/table.html:57
#: templates/transactions/fragments/list_all.html:43
#: templates/transactions/fragments/list_all.html:70
msgid "Page navigation"
msgstr "Navigation dans les pages"
@@ -2679,15 +2707,22 @@ msgstr "Ciblage"
msgid "Last fetch"
msgstr "Dernière récupération"
#: templates/exchange_rates_services/fragments/list.html:61
#: templates/exchange_rates_services/fragments/list.html:62
#, python-format
msgid "%(counter)s consecutive failure"
msgid_plural "%(counter)s consecutive failures"
msgstr[0] ""
msgstr[1] ""
#: templates/exchange_rates_services/fragments/list.html:69
msgid "currencies"
msgstr "devises"
#: templates/exchange_rates_services/fragments/list.html:61
#: templates/exchange_rates_services/fragments/list.html:69
msgid "accounts"
msgstr "Comptes"
#: templates/exchange_rates_services/fragments/list.html:69
#: templates/exchange_rates_services/fragments/list.html:77
msgid "No services configured"
msgstr "Pas de services configurés"
@@ -2946,7 +2981,11 @@ msgid "Final total"
msgstr "Total final"
#: templates/insights/fragments/category_overview/index.html:89
#: templates/monthly_overview/fragments/monthly_summary.html:165
#: templates/insights/fragments/month_by_month.html:91
#: templates/insights/fragments/month_by_month.html:186
#: templates/insights/fragments/year_by_year.html:59
#: templates/insights/fragments/year_by_year.html:140
#: templates/monthly_overview/fragments/monthly_summary.html:167
msgid "Total"
msgstr "Total"
@@ -2990,6 +3029,75 @@ msgstr "Aucunes transactions en retard"
msgid "No recent transactions"
msgstr "Aucunes transactions récentes"
#: templates/insights/fragments/month_by_month.html:86
#: templates/insights/fragments/year_by_year.html:54
#, fuzzy
#| msgid "Tags"
msgid "Tag"
msgstr "Etiquettes"
#: templates/insights/fragments/month_by_month.html:94
msgid "Jan"
msgstr ""
#: templates/insights/fragments/month_by_month.html:95
msgid "Feb"
msgstr ""
#: templates/insights/fragments/month_by_month.html:96
#, fuzzy
#| msgid "Max"
msgid "Mar"
msgstr "Max"
#: templates/insights/fragments/month_by_month.html:97
msgid "Apr"
msgstr ""
#: templates/insights/fragments/month_by_month.html:98
#, fuzzy
#| msgid "Max"
msgid "May"
msgstr "Max"
#: templates/insights/fragments/month_by_month.html:99
msgid "Jun"
msgstr ""
#: templates/insights/fragments/month_by_month.html:100
msgid "Jul"
msgstr ""
#: templates/insights/fragments/month_by_month.html:101
msgid "Aug"
msgstr ""
#: templates/insights/fragments/month_by_month.html:102
#, fuzzy
#| msgid "Set"
msgid "Sep"
msgstr "Définir"
#: templates/insights/fragments/month_by_month.html:103
msgid "Oct"
msgstr ""
#: templates/insights/fragments/month_by_month.html:104
#, fuzzy
#| msgid "Now"
msgid "Nov"
msgstr "Maintenant"
#: templates/insights/fragments/month_by_month.html:105
msgid "Dec"
msgstr ""
#: templates/insights/fragments/month_by_month.html:248
#, fuzzy
#| msgid "No transactions on this date"
msgid "No transactions for this year"
msgstr "Aucunes transactions à cette date"
#: templates/insights/fragments/sankey.html:100
msgid "From"
msgstr "De"
@@ -2998,6 +3106,12 @@ msgstr "De"
msgid "Percentage"
msgstr "Pourcentage"
#: templates/insights/fragments/year_by_year.html:202
#, fuzzy
#| msgid "transactions"
msgid "No transactions"
msgstr "transactions"
#: templates/insights/pages/index.html:37
msgid "Month"
msgstr "Mois"
@@ -3047,6 +3161,16 @@ msgstr "Transactions récentes"
msgid "Emergency Fund"
msgstr "Fonds de secours"
#: templates/insights/pages/index.html:127
#, fuzzy
#| msgid "Yearly by account"
msgid "Year by Year"
msgstr "Annuel par comptes"
#: templates/insights/pages/index.html:132
msgid "Month by Month"
msgstr ""
#: templates/installment_plans/fragments/add.html:5
msgid "Add installment plan"
msgstr "Ajouter un paiement en plusieurs fois"
@@ -3122,35 +3246,40 @@ msgstr "Prix unitaire"
msgid "Item"
msgstr "Eléments"
#: templates/monthly_overview/fragments/list.html:32
#: templates/monthly_overview/fragments/list.html:15
#: templates/transactions/fragments/list_all.html:15
msgid "late"
msgstr ""
#: templates/monthly_overview/fragments/list.html:58
msgid "No transactions this month"
msgstr "Aucunes transactions ce mois-ci"
#: templates/monthly_overview/fragments/monthly_summary.html:6
#: templates/monthly_overview/fragments/monthly_summary.html:7
msgid "Daily Spending Allowance"
msgstr "Montant disponible pour les dépenses quotidiennes"
#: templates/monthly_overview/fragments/monthly_summary.html:6
#: templates/monthly_overview/fragments/monthly_summary.html:7
msgid "This is the final total divided by the remaining days in the month"
msgstr "Total final divisé par le nombre de jour restant dans le mois"
#: templates/monthly_overview/fragments/monthly_summary.html:42
#: templates/monthly_overview/fragments/monthly_summary.html:105
#: templates/monthly_overview/fragments/monthly_summary.html:168
#: templates/monthly_overview/fragments/monthly_summary.html:44
#: templates/monthly_overview/fragments/monthly_summary.html:107
#: templates/monthly_overview/fragments/monthly_summary.html:170
msgid "current"
msgstr "A date"
#: templates/monthly_overview/fragments/monthly_summary.html:71
#: templates/monthly_overview/fragments/monthly_summary.html:134
#: templates/monthly_overview/fragments/monthly_summary.html:197
#: templates/monthly_overview/fragments/monthly_summary.html:73
#: templates/monthly_overview/fragments/monthly_summary.html:136
#: templates/monthly_overview/fragments/monthly_summary.html:199
msgid "projected"
msgstr "Prévisionnel"
#: templates/monthly_overview/fragments/monthly_summary.html:102
#: templates/monthly_overview/fragments/monthly_summary.html:104
msgid "Expenses"
msgstr "Dépenses"
#: templates/monthly_overview/fragments/monthly_summary.html:255
#: templates/monthly_overview/fragments/monthly_summary.html:257
msgid "Distribution"
msgstr "Répartition"
@@ -3158,27 +3287,27 @@ msgstr "Répartition"
msgid "Summary"
msgstr "Résumé"
#: templates/monthly_overview/pages/overview.html:96
#: templates/monthly_overview/pages/overview.html:150
#: templates/monthly_overview/pages/overview.html:99
#: templates/monthly_overview/pages/overview.html:254
#: templates/transactions/pages/transactions.html:48
#: templates/transactions/pages/transactions.html:103
#: templates/transactions/pages/transactions.html:204
msgid "Oldest first"
msgstr "Plus ancien en premier"
#: templates/monthly_overview/pages/overview.html:97
#: templates/monthly_overview/pages/overview.html:159
#: templates/monthly_overview/pages/overview.html:100
#: templates/monthly_overview/pages/overview.html:263
#: templates/transactions/pages/transactions.html:49
#: templates/transactions/pages/transactions.html:112
#: templates/transactions/pages/transactions.html:213
msgid "Newest first"
msgstr "Plus récent en premier"
#: templates/monthly_overview/pages/overview.html:106
#: templates/monthly_overview/pages/overview.html:109
#: templates/transactions/pages/transactions.html:58
msgid "Filter transactions"
msgstr "Filtrer les transactions"
#: templates/monthly_overview/pages/overview.html:131
#: templates/transactions/pages/transactions.html:84
#: templates/monthly_overview/pages/overview.html:235
#: templates/transactions/pages/transactions.html:185
msgid "Order by"
msgstr "Trier par"
@@ -3428,7 +3557,7 @@ msgstr "Modification en cours"
msgid "transactions"
msgstr "transactions"
#: templates/transactions/fragments/list_all.html:32
#: templates/transactions/fragments/list_all.html:58
msgid "No transactions found"
msgstr "Aucunes transactions trouvées"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-12-20 02:59+0000\n"
"POT-Creation-Date: 2026-01-10 20:50+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
@@ -65,24 +65,30 @@ msgstr ""
#: apps/transactions/forms.py:419 apps/transactions/forms.py:516
#: apps/transactions/forms.py:523 apps/transactions/forms.py:707
#: apps/transactions/forms.py:948 apps/transactions/models.py:322
#: apps/transactions/models.py:574 apps/transactions/models.py:774
#: apps/transactions/models.py:1022
#: apps/transactions/models.py:578 apps/transactions/models.py:778
#: apps/transactions/models.py:1026
#: templates/insights/fragments/category_overview/index.html:86
#: templates/insights/fragments/category_overview/index.html:542
#: templates/insights/fragments/month_by_month.html:84
#: templates/insights/fragments/year_by_year.html:52
msgid "Category"
msgstr ""
#: apps/accounts/forms.py:132 apps/dca/forms.py:95 apps/dca/forms.py:103
#: apps/export_app/forms.py:43 apps/export_app/forms.py:132
#: apps/rules/forms.py:184 apps/rules/forms.py:194 apps/rules/models.py:45
#: apps/rules/models.py:315 apps/transactions/filters.py:68
#: apps/rules/models.py:315 apps/transactions/filters.py:73
#: apps/transactions/forms.py:51 apps/transactions/forms.py:259
#: apps/transactions/forms.py:427 apps/transactions/forms.py:532
#: apps/transactions/forms.py:540 apps/transactions/forms.py:700
#: apps/transactions/forms.py:941 apps/transactions/models.py:328
#: apps/transactions/models.py:576 apps/transactions/models.py:778
#: apps/transactions/models.py:1028 templates/includes/sidebar.html:150
#: apps/transactions/models.py:580 apps/transactions/models.py:782
#: apps/transactions/models.py:1032 templates/includes/sidebar.html:150
#: templates/insights/fragments/category_overview/index.html:40
#: templates/insights/fragments/month_by_month.html:29
#: templates/insights/fragments/month_by_month.html:32
#: templates/insights/fragments/year_by_year.html:24
#: templates/insights/fragments/year_by_year.html:27
#: templates/tags/fragments/list.html:9 templates/tags/pages/index.html:4
msgid "Tags"
msgstr ""
@@ -90,7 +96,7 @@ msgstr ""
#: apps/accounts/models.py:12 apps/accounts/models.py:29 apps/dca/models.py:13
#: apps/import_app/models.py:14 apps/rules/models.py:13
#: apps/transactions/models.py:214 apps/transactions/models.py:239
#: apps/transactions/models.py:263 apps/transactions/models.py:990
#: apps/transactions/models.py:263 apps/transactions/models.py:994
#: templates/account_groups/fragments/list.html:22
#: templates/accounts/fragments/list.html:22
#: templates/categories/fragments/table.html:17
@@ -159,8 +165,8 @@ msgstr ""
#: apps/transactions/forms.py:63 apps/transactions/forms.py:271
#: apps/transactions/forms.py:386 apps/transactions/forms.py:692
#: apps/transactions/forms.py:933 apps/transactions/models.py:294
#: apps/transactions/models.py:534 apps/transactions/models.py:756
#: apps/transactions/models.py:996
#: apps/transactions/models.py:538 apps/transactions/models.py:760
#: apps/transactions/models.py:1000
#: templates/installment_plans/fragments/table.html:17
#: templates/quick_transactions/fragments/list.html:14
#: templates/recurring_transactions/fragments/table.html:19
@@ -169,11 +175,11 @@ msgid "Account"
msgstr ""
#: apps/accounts/models.py:76 apps/export_app/forms.py:19
#: apps/export_app/forms.py:129 apps/transactions/filters.py:52
#: apps/export_app/forms.py:129 apps/transactions/filters.py:57
#: templates/accounts/fragments/list.html:9
#: templates/accounts/pages/index.html:4 templates/includes/sidebar.html:162
#: templates/includes/sidebar.html:164
#: templates/monthly_overview/pages/overview.html:75
#: templates/monthly_overview/pages/overview.html:77
#: templates/transactions/pages/transactions.html:26
msgid "Accounts"
msgstr ""
@@ -188,8 +194,8 @@ msgstr ""
#: apps/accounts/views/account_groups.py:69
#: apps/accounts/views/account_groups.py:152 apps/accounts/views/accounts.py:68
#: apps/accounts/views/accounts.py:106 apps/dca/views.py:63
#: apps/dca/views.py:146 apps/rules/views.py:118 apps/rules/views.py:228
#: apps/accounts/views/accounts.py:106 apps/dca/views.py:62
#: apps/dca/views.py:145 apps/rules/views.py:118 apps/rules/views.py:228
#: apps/transactions/views/categories.py:91
#: apps/transactions/views/categories.py:129
#: apps/transactions/views/entities.py:91
@@ -203,7 +209,7 @@ msgid "Account Group updated successfully"
msgstr ""
#: apps/accounts/views/account_groups.py:111
#: apps/accounts/views/accounts.py:145 apps/dca/views.py:105
#: apps/accounts/views/accounts.py:145 apps/dca/views.py:104
#: apps/rules/views.py:185 apps/transactions/views/categories.py:168
#: apps/transactions/views/entities.py:130 apps/transactions/views/tags.py:130
msgid "Item no longer shared with you"
@@ -214,14 +220,14 @@ msgid "Account Group deleted successfully"
msgstr ""
#: apps/accounts/views/account_groups.py:135
#: apps/accounts/views/accounts.py:189 apps/dca/views.py:129
#: apps/accounts/views/accounts.py:189 apps/dca/views.py:128
#: apps/rules/views.py:210 apps/transactions/views/categories.py:192
#: apps/transactions/views/entities.py:154 apps/transactions/views/tags.py:154
msgid "Ownership taken successfully"
msgstr ""
#: apps/accounts/views/account_groups.py:165
#: apps/accounts/views/accounts.py:119 apps/dca/views.py:159
#: apps/accounts/views/accounts.py:119 apps/dca/views.py:158
#: apps/rules/views.py:241 apps/transactions/views/categories.py:142
#: apps/transactions/views/entities.py:184 apps/transactions/views/tags.py:184
msgid "Configuration saved successfully"
@@ -354,7 +360,7 @@ msgid "Public"
msgstr ""
#: apps/common/templatetags/natural.py:20
#: templates/monthly_overview/fragments/monthly_summary.html:9
#: templates/monthly_overview/fragments/monthly_summary.html:10
msgid "today"
msgstr ""
@@ -452,10 +458,10 @@ msgstr ""
#: apps/common/widgets/tom_select.py:15
#: templates/mini_tools/unit_price_calculator.html:180
#: templates/monthly_overview/pages/overview.html:171
#: templates/monthly_overview/pages/overview.html:183
#: templates/transactions/pages/transactions.html:124
#: templates/transactions/pages/transactions.html:136
#: templates/monthly_overview/pages/overview.html:275
#: templates/monthly_overview/pages/overview.html:287
#: templates/transactions/pages/transactions.html:225
#: templates/transactions/pages/transactions.html:237
msgid "Clear"
msgstr ""
@@ -494,11 +500,11 @@ msgid "Decimal Places"
msgstr ""
#: apps/currencies/models.py:45 apps/export_app/forms.py:25
#: apps/export_app/forms.py:130 apps/transactions/filters.py:59
#: apps/export_app/forms.py:130 apps/transactions/filters.py:64
#: templates/currencies/fragments/list.html:9
#: templates/currencies/pages/index.html:4 templates/includes/sidebar.html:176
#: templates/includes/sidebar.html:178
#: templates/monthly_overview/pages/overview.html:62
#: templates/monthly_overview/pages/overview.html:63
#: templates/transactions/pages/transactions.html:12
msgid "Currencies"
msgstr ""
@@ -559,9 +565,9 @@ msgstr ""
msgid "Service Type"
msgstr ""
#: apps/currencies/models.py:118 apps/transactions/models.py:218
#: apps/transactions/models.py:242 apps/transactions/models.py:266
#: templates/categories/fragments/list.html:16
#: apps/currencies/models.py:118 apps/transactions/filters.py:27
#: apps/transactions/models.py:218 apps/transactions/models.py:242
#: apps/transactions/models.py:266 templates/categories/fragments/list.html:16
#: templates/entities/fragments/list.html:16
#: templates/installment_plans/fragments/list.html:16
#: templates/recurring_transactions/fragments/list.html:16
@@ -589,57 +595,57 @@ msgstr ""
msgid "Last Successful Fetch"
msgstr ""
#: apps/currencies/models.py:141
#: apps/currencies/models.py:143
msgid "Target Currencies"
msgstr ""
#: apps/currencies/models.py:143
#: apps/currencies/models.py:145
msgid ""
"Select currencies to fetch exchange rates for. Rates will be fetched for "
"each currency against their set exchange currency."
msgstr ""
#: apps/currencies/models.py:151
#: apps/currencies/models.py:153
msgid "Target Accounts"
msgstr ""
#: apps/currencies/models.py:153
#: apps/currencies/models.py:155
msgid ""
"Select accounts to fetch exchange rates for. Rates will be fetched for each "
"account's currency against their set exchange currency."
msgstr ""
#: apps/currencies/models.py:160
#: apps/currencies/models.py:162
msgid "Single exchange rate"
msgstr ""
#: apps/currencies/models.py:163
#: apps/currencies/models.py:165
msgid "Create one exchange rate and keep updating it. Avoids database clutter."
msgstr ""
#: apps/currencies/models.py:168
#: apps/currencies/models.py:170
msgid "Exchange Rate Service"
msgstr ""
#: apps/currencies/models.py:169
#: apps/currencies/models.py:171
msgid "Exchange Rate Services"
msgstr ""
#: apps/currencies/models.py:221
#: apps/currencies/models.py:223
msgid "'Every X hours' interval type requires a positive integer."
msgstr ""
#: apps/currencies/models.py:230
#: apps/currencies/models.py:232
msgid "'Every X hours' interval must be between 1 and 24."
msgstr ""
#: apps/currencies/models.py:244
#: apps/currencies/models.py:246
msgid ""
"Invalid hour format. Use comma-separated hours (0-23) and/or ranges (e.g., "
"'1-5,8,10-12')."
msgstr ""
#: apps/currencies/models.py:255
#: apps/currencies/models.py:257
msgid ""
"Invalid format. Please check the requirements for your selected interval "
"type."
@@ -738,8 +744,8 @@ msgstr ""
#: apps/dca/models.py:26 apps/dca/models.py:181 apps/rules/forms.py:180
#: apps/rules/forms.py:196 apps/rules/models.py:43 apps/rules/models.py:295
#: apps/transactions/forms.py:413 apps/transactions/forms.py:560
#: apps/transactions/models.py:318 apps/transactions/models.py:583
#: apps/transactions/models.py:784 apps/transactions/models.py:1018
#: apps/transactions/models.py:318 apps/transactions/models.py:587
#: apps/transactions/models.py:788 apps/transactions/models.py:1022
msgid "Notes"
msgstr ""
@@ -771,27 +777,27 @@ msgstr ""
msgid "DCA Entries"
msgstr ""
#: apps/dca/views.py:39
#: apps/dca/views.py:38
msgid "DCA Strategy added successfully"
msgstr ""
#: apps/dca/views.py:76
#: apps/dca/views.py:75
msgid "DCA Strategy updated successfully"
msgstr ""
#: apps/dca/views.py:108
#: apps/dca/views.py:107
msgid "DCA strategy deleted successfully"
msgstr ""
#: apps/dca/views.py:238
#: apps/dca/views.py:237
msgid "Entry added successfully"
msgstr ""
#: apps/dca/views.py:265
#: apps/dca/views.py:264
msgid "Entry updated successfully"
msgstr ""
#: apps/dca/views.py:291
#: apps/dca/views.py:290
msgid "Entry deleted successfully"
msgstr ""
@@ -811,34 +817,42 @@ msgid "Transactions"
msgstr ""
#: apps/export_app/forms.py:37 apps/export_app/forms.py:131
#: apps/transactions/filters.py:63 templates/categories/fragments/list.html:9
#: apps/transactions/filters.py:68 templates/categories/fragments/list.html:9
#: templates/categories/pages/index.html:4 templates/includes/sidebar.html:144
#: templates/insights/fragments/month_by_month.html:18
#: templates/insights/fragments/month_by_month.html:21
#: templates/insights/fragments/year_by_year.html:13
#: templates/insights/fragments/year_by_year.html:16
msgid "Categories"
msgstr ""
#: apps/export_app/forms.py:49 apps/export_app/forms.py:133
#: apps/rules/forms.py:185 apps/rules/forms.py:195 apps/rules/models.py:46
#: apps/rules/models.py:307 apps/transactions/filters.py:73
#: apps/rules/models.py:307 apps/transactions/filters.py:78
#: apps/transactions/forms.py:59 apps/transactions/forms.py:267
#: apps/transactions/forms.py:435 apps/transactions/forms.py:715
#: apps/transactions/forms.py:956 apps/transactions/models.py:277
#: apps/transactions/models.py:333 apps/transactions/models.py:579
#: apps/transactions/models.py:781 apps/transactions/models.py:1033
#: apps/transactions/models.py:333 apps/transactions/models.py:583
#: apps/transactions/models.py:785 apps/transactions/models.py:1037
#: templates/entities/fragments/list.html:9
#: templates/entities/pages/index.html:4 templates/includes/sidebar.html:156
#: templates/insights/fragments/category_overview/index.html:54
#: templates/insights/fragments/month_by_month.html:40
#: templates/insights/fragments/month_by_month.html:43
#: templates/insights/fragments/year_by_year.html:35
#: templates/insights/fragments/year_by_year.html:38
msgid "Entities"
msgstr ""
#: apps/export_app/forms.py:55 apps/export_app/forms.py:137
#: apps/transactions/models.py:821 templates/includes/sidebar.html:110
#: apps/transactions/models.py:825 templates/includes/sidebar.html:110
#: templates/recurring_transactions/fragments/list.html:9
#: templates/recurring_transactions/pages/index.html:4
msgid "Recurring Transactions"
msgstr ""
#: apps/export_app/forms.py:61 apps/export_app/forms.py:135
#: apps/transactions/models.py:597 templates/includes/sidebar.html:104
#: apps/transactions/models.py:601 templates/includes/sidebar.html:104
#: templates/installment_plans/fragments/list.html:9
#: templates/installment_plans/pages/index.html:4
msgid "Installment Plans"
@@ -990,10 +1004,12 @@ msgid "Run deleted successfully"
msgstr ""
#: apps/insights/forms.py:118 apps/insights/utils/sankey.py:36
#: apps/insights/utils/sankey.py:167 apps/transactions/filters.py:186
#: apps/insights/utils/sankey.py:167 apps/transactions/filters.py:203
#: templates/insights/fragments/category_overview/index.html:96
#: templates/insights/fragments/category_overview/index.html:407
#: templates/insights/fragments/category_overview/index.html:436
#: templates/insights/fragments/month_by_month.html:119
#: templates/insights/fragments/year_by_year.html:73
msgid "Uncategorized"
msgstr ""
@@ -1086,15 +1102,15 @@ msgstr ""
#: apps/rules/forms.py:174 apps/rules/forms.py:188 apps/rules/models.py:36
#: apps/rules/models.py:271 apps/transactions/forms.py:377
#: apps/transactions/models.py:301 apps/transactions/models.py:539
#: apps/transactions/models.py:762 apps/transactions/models.py:1003
#: apps/transactions/models.py:301 apps/transactions/models.py:543
#: apps/transactions/models.py:766 apps/transactions/models.py:1007
msgid "Type"
msgstr ""
#: apps/rules/forms.py:175 apps/rules/forms.py:189 apps/rules/models.py:37
#: apps/rules/models.py:275 apps/transactions/filters.py:22
#: apps/transactions/forms.py:381 apps/transactions/models.py:303
#: apps/transactions/models.py:1005 templates/cotton/transaction/item.html:20
#: apps/transactions/models.py:1009 templates/cotton/transaction/item.html:20
#: templates/cotton/transaction/item.html:31
#: templates/transactions/widgets/paid_toggle_button.html:10
#: templates/transactions/widgets/unselectable_paid_toggle_button.html:13
@@ -1105,14 +1121,14 @@ msgstr ""
#: apps/rules/models.py:283 apps/transactions/forms.py:71
#: apps/transactions/forms.py:397 apps/transactions/forms.py:547
#: apps/transactions/forms.py:721 apps/transactions/models.py:305
#: apps/transactions/models.py:557 apps/transactions/models.py:786
#: apps/transactions/models.py:561 apps/transactions/models.py:790
msgid "Reference Date"
msgstr ""
#: apps/rules/forms.py:178 apps/rules/forms.py:192 apps/rules/models.py:41
#: apps/rules/models.py:287 apps/transactions/forms.py:404
#: apps/transactions/models.py:311 apps/transactions/models.py:767
#: apps/transactions/models.py:1011
#: apps/transactions/models.py:311 apps/transactions/models.py:771
#: apps/transactions/models.py:1015
#: templates/insights/fragments/sankey.html:102
#: templates/installment_plans/fragments/table.html:18
#: templates/quick_transactions/fragments/list.html:15
@@ -1123,27 +1139,27 @@ msgstr ""
#: apps/rules/forms.py:179 apps/rules/forms.py:193 apps/rules/models.py:14
#: apps/rules/models.py:42 apps/rules/models.py:291
#: apps/transactions/forms.py:408 apps/transactions/forms.py:551
#: apps/transactions/models.py:316 apps/transactions/models.py:541
#: apps/transactions/models.py:770 apps/transactions/models.py:1016
#: apps/transactions/models.py:316 apps/transactions/models.py:545
#: apps/transactions/models.py:774 apps/transactions/models.py:1020
msgid "Description"
msgstr ""
#: apps/rules/forms.py:182 apps/rules/forms.py:198 apps/rules/models.py:47
#: apps/rules/models.py:299 apps/transactions/models.py:355
#: apps/transactions/models.py:1038
#: apps/transactions/models.py:1042
msgid "Internal Note"
msgstr ""
#: apps/rules/forms.py:183 apps/rules/forms.py:199 apps/rules/models.py:48
#: apps/rules/models.py:303 apps/transactions/models.py:357
#: apps/transactions/models.py:1040
#: apps/transactions/models.py:1044
msgid "Internal ID"
msgstr ""
#: apps/rules/forms.py:186 apps/rules/forms.py:200 apps/rules/models.py:40
#: apps/rules/models.py:319 apps/transactions/forms.py:564
#: apps/transactions/models.py:215 apps/transactions/models.py:306
#: apps/transactions/models.py:1006
#: apps/transactions/models.py:1010
msgid "Mute"
msgstr ""
@@ -1299,53 +1315,65 @@ msgstr ""
msgid "Projected"
msgstr ""
#: apps/transactions/filters.py:40
#: apps/transactions/filters.py:28 templates/categories/fragments/table.html:18
msgid "Muted"
msgstr ""
#: apps/transactions/filters.py:45
msgid "Content"
msgstr ""
#: apps/transactions/filters.py:46
#: apps/transactions/filters.py:51
msgid "Transaction Type"
msgstr ""
#: apps/transactions/filters.py:84
msgid "Date from"
msgstr ""
#: apps/transactions/filters.py:89 apps/transactions/filters.py:99
msgid "Until"
#: apps/transactions/filters.py:89
msgid "Mute Status"
msgstr ""
#: apps/transactions/filters.py:94
msgid "Reference date from"
msgid "Date from"
msgstr ""
#: apps/transactions/filters.py:99 apps/transactions/filters.py:109
msgid "Until"
msgstr ""
#: apps/transactions/filters.py:104
msgid "Reference date from"
msgstr ""
#: apps/transactions/filters.py:114
msgid "Amount min"
msgstr ""
#: apps/transactions/filters.py:109
#: apps/transactions/filters.py:119
msgid "Amount max"
msgstr ""
#: apps/transactions/filters.py:185
#: apps/transactions/filters.py:202
msgid "Categorized"
msgstr ""
#: apps/transactions/filters.py:192
#: apps/transactions/filters.py:209
msgid "Tagged"
msgstr ""
#: apps/transactions/filters.py:192
#: apps/transactions/filters.py:209
#: templates/insights/fragments/category_overview/index.html:189
#: templates/insights/fragments/month_by_month.html:121
#: templates/insights/fragments/year_by_year.html:75
msgid "Untagged"
msgstr ""
#: apps/transactions/filters.py:198
#: apps/transactions/filters.py:215
msgid "Any entity"
msgstr ""
#: apps/transactions/filters.py:199
#: apps/transactions/filters.py:216
#: templates/insights/fragments/category_overview/index.html:282
#: templates/insights/fragments/month_by_month.html:123
#: templates/insights/fragments/year_by_year.html:77
msgid "No entity"
msgstr ""
@@ -1433,10 +1461,12 @@ msgid ""
msgstr ""
#: apps/transactions/models.py:276
#: templates/insights/fragments/month_by_month.html:88
#: templates/insights/fragments/year_by_year.html:56
msgid "Entity"
msgstr ""
#: apps/transactions/models.py:288 apps/transactions/models.py:983
#: apps/transactions/models.py:288 apps/transactions/models.py:987
#: templates/calendar_view/fragments/list.html:42
#: templates/calendar_view/fragments/list.html:44
#: templates/calendar_view/fragments/list.html:52
@@ -1444,11 +1474,11 @@ msgstr ""
#: templates/cotton/ui/quick_transactions_buttons.html:10
#: templates/cotton/ui/transactions_fab.html:10
#: templates/insights/fragments/category_overview/index.html:87
#: templates/monthly_overview/fragments/monthly_summary.html:39
#: templates/monthly_overview/fragments/monthly_summary.html:41
msgid "Income"
msgstr ""
#: apps/transactions/models.py:289 apps/transactions/models.py:984
#: apps/transactions/models.py:289 apps/transactions/models.py:988
#: templates/calendar_view/fragments/list.html:46
#: templates/calendar_view/fragments/list.html:48
#: templates/calendar_view/fragments/list.html:56
@@ -1459,11 +1489,11 @@ msgstr ""
msgid "Expense"
msgstr ""
#: apps/transactions/models.py:344 apps/transactions/models.py:596
#: apps/transactions/models.py:344 apps/transactions/models.py:600
msgid "Installment Plan"
msgstr ""
#: apps/transactions/models.py:353 apps/transactions/models.py:820
#: apps/transactions/models.py:353 apps/transactions/models.py:824
msgid "Recurring Transaction"
msgstr ""
@@ -1475,113 +1505,113 @@ msgstr ""
msgid "Deleted At"
msgstr ""
#: apps/transactions/models.py:476 templates/tags/fragments/table.html:69
#: apps/transactions/models.py:480 templates/tags/fragments/table.html:69
msgid "No tags"
msgstr ""
#: apps/transactions/models.py:478
#: apps/transactions/models.py:482
msgid "No category"
msgstr ""
#: apps/transactions/models.py:480
#: apps/transactions/models.py:484
msgid "No description"
msgstr ""
#: apps/transactions/models.py:528 templates/includes/sidebar.html:57
#: apps/transactions/models.py:532 templates/includes/sidebar.html:57
msgid "Yearly"
msgstr ""
#: apps/transactions/models.py:529 apps/users/models.py:464
#: apps/transactions/models.py:533 apps/users/models.py:464
#: templates/includes/sidebar.html:51
msgid "Monthly"
msgstr ""
#: apps/transactions/models.py:530
#: apps/transactions/models.py:534
msgid "Weekly"
msgstr ""
#: apps/transactions/models.py:531
#: apps/transactions/models.py:535
msgid "Daily"
msgstr ""
#: apps/transactions/models.py:544
#: apps/transactions/models.py:548
msgid "Number of Installments"
msgstr ""
#: apps/transactions/models.py:549
#: apps/transactions/models.py:553
msgid "Installment Start"
msgstr ""
#: apps/transactions/models.py:550
#: apps/transactions/models.py:554
msgid "The installment number to start counting from"
msgstr ""
#: apps/transactions/models.py:555 apps/transactions/models.py:790
#: apps/transactions/models.py:559 apps/transactions/models.py:794
msgid "Start Date"
msgstr ""
#: apps/transactions/models.py:559 apps/transactions/models.py:791
#: apps/transactions/models.py:563 apps/transactions/models.py:795
msgid "End Date"
msgstr ""
#: apps/transactions/models.py:564
#: apps/transactions/models.py:568
msgid "Recurrence"
msgstr ""
#: apps/transactions/models.py:567
#: apps/transactions/models.py:571
msgid "Installment Amount"
msgstr ""
#: apps/transactions/models.py:586 apps/transactions/models.py:810
#: apps/transactions/models.py:590 apps/transactions/models.py:814
msgid "Add description to transactions"
msgstr ""
#: apps/transactions/models.py:589 apps/transactions/models.py:813
#: apps/transactions/models.py:593 apps/transactions/models.py:817
msgid "Add notes to transactions"
msgstr ""
#: apps/transactions/models.py:749
#: apps/transactions/models.py:753
msgid "day(s)"
msgstr ""
#: apps/transactions/models.py:750
#: apps/transactions/models.py:754
msgid "week(s)"
msgstr ""
#: apps/transactions/models.py:751
#: apps/transactions/models.py:755
msgid "month(s)"
msgstr ""
#: apps/transactions/models.py:752
#: apps/transactions/models.py:756
msgid "year(s)"
msgstr ""
#: apps/transactions/models.py:754
#: apps/transactions/models.py:758
#: templates/recurring_transactions/fragments/list.html:18
msgid "Paused"
msgstr ""
#: apps/transactions/models.py:793
#: apps/transactions/models.py:797
msgid "Recurrence Type"
msgstr ""
#: apps/transactions/models.py:796
#: apps/transactions/models.py:800
msgid "Recurrence Interval"
msgstr ""
#: apps/transactions/models.py:799
#: apps/transactions/models.py:803
msgid "Keep at most"
msgstr ""
#: apps/transactions/models.py:803
#: apps/transactions/models.py:807
msgid "Last Generated Date"
msgstr ""
#: apps/transactions/models.py:806
#: apps/transactions/models.py:810
msgid "Last Generated Reference Date"
msgstr ""
#: apps/transactions/models.py:1050
#: apps/transactions/models.py:1054
#: apps/transactions/views/quick_transactions.py:178
#: apps/transactions/views/quick_transactions.py:187
#: apps/transactions/views/quick_transactions.py:189
@@ -1590,7 +1620,7 @@ msgstr ""
msgid "Quick Transaction"
msgstr ""
#: apps/transactions/models.py:1051 templates/includes/sidebar.html:98
#: apps/transactions/models.py:1055 templates/includes/sidebar.html:98
#: templates/quick_transactions/pages/index.html:5
#: templates/quick_transactions/pages/index.html:15
msgid "Quick Transactions"
@@ -1696,7 +1726,7 @@ msgstr ""
#: apps/transactions/views/quick_transactions.py:156
#: apps/transactions/views/transactions.py:53
#: apps/transactions/views/transactions.py:228
#: apps/transactions/views/transactions.py:238
msgid "Transaction added successfully"
msgstr ""
@@ -1736,30 +1766,30 @@ msgstr ""
msgid "Tag deleted successfully"
msgstr ""
#: apps/transactions/views/transactions.py:252
#: apps/transactions/views/transactions.py:262
msgid "Transaction updated successfully"
msgstr ""
#: apps/transactions/views/transactions.py:303
#: apps/transactions/views/transactions.py:313
#, python-format
msgid "%(count)s transaction updated successfully"
msgid_plural "%(count)s transactions updated successfully"
msgstr[0] ""
msgstr[1] ""
#: apps/transactions/views/transactions.py:339
#: apps/transactions/views/transactions.py:349
msgid "Transaction duplicated successfully"
msgstr ""
#: apps/transactions/views/transactions.py:381
#: apps/transactions/views/transactions.py:391
msgid "Transaction deleted successfully"
msgstr ""
#: apps/transactions/views/transactions.py:399
#: apps/transactions/views/transactions.py:409
msgid "Transaction restored successfully"
msgstr ""
#: apps/transactions/views/transactions.py:425
#: apps/transactions/views/transactions.py:435
msgid "Transfer added successfully"
msgstr ""
@@ -1801,10 +1831,10 @@ msgid "This account is deactivated"
msgstr ""
#: apps/users/forms.py:62 apps/users/forms.py:75 apps/users/forms.py:97
#: templates/monthly_overview/pages/overview.html:95
#: templates/monthly_overview/pages/overview.html:141
#: templates/monthly_overview/pages/overview.html:98
#: templates/monthly_overview/pages/overview.html:245
#: templates/transactions/pages/transactions.html:47
#: templates/transactions/pages/transactions.html:94
#: templates/transactions/pages/transactions.html:195
msgid "Default"
msgstr ""
@@ -2224,10 +2254,6 @@ msgstr ""
msgid "Edit category"
msgstr ""
#: templates/categories/fragments/table.html:18
msgid "Muted"
msgstr ""
#: templates/categories/fragments/table.html:73
#: templates/insights/fragments/category_overview/index.html:552
msgid "No categories"
@@ -2246,9 +2272,9 @@ msgstr ""
#: templates/cotton/config/search.html:6
#: templates/import_app/fragments/profiles/list_presets.html:13
#: templates/monthly_overview/pages/overview.html:115
#: templates/monthly_overview/pages/overview.html:219
#: templates/rules/fragments/transaction_rule/dry_run/visual.html:57
#: templates/transactions/pages/transactions.html:67
#: templates/transactions/pages/transactions.html:168
msgid "Search"
msgstr ""
@@ -2476,8 +2502,8 @@ msgid "No entries for this DCA"
msgstr ""
#: templates/dca/fragments/strategy/details.html:120
#: templates/monthly_overview/fragments/list.html:33
#: templates/transactions/fragments/list_all.html:33
#: templates/monthly_overview/fragments/list.html:59
#: templates/transactions/fragments/list_all.html:59
msgid "Try adding one"
msgstr ""
@@ -2602,7 +2628,7 @@ msgstr ""
#: templates/exchange_rates/fragments/table.html:56
#: templates/exchange_rates_services/fragments/table.html:57
#: templates/transactions/fragments/list_all.html:43
#: templates/transactions/fragments/list_all.html:70
msgid "Page navigation"
msgstr ""
@@ -2622,15 +2648,21 @@ msgstr ""
msgid "Last fetch"
msgstr ""
#: templates/exchange_rates_services/fragments/list.html:61
#: templates/exchange_rates_services/fragments/list.html:62
#, python-format
msgid "%(counter)s consecutive failure"
msgid_plural "%(counter)s consecutive failures"
msgstr[0] ""
#: templates/exchange_rates_services/fragments/list.html:69
msgid "currencies"
msgstr ""
#: templates/exchange_rates_services/fragments/list.html:61
#: templates/exchange_rates_services/fragments/list.html:69
msgid "accounts"
msgstr ""
#: templates/exchange_rates_services/fragments/list.html:69
#: templates/exchange_rates_services/fragments/list.html:77
msgid "No services configured"
msgstr ""
@@ -2880,7 +2912,11 @@ msgid "Final total"
msgstr ""
#: templates/insights/fragments/category_overview/index.html:89
#: templates/monthly_overview/fragments/monthly_summary.html:165
#: templates/insights/fragments/month_by_month.html:91
#: templates/insights/fragments/month_by_month.html:186
#: templates/insights/fragments/year_by_year.html:59
#: templates/insights/fragments/year_by_year.html:140
#: templates/monthly_overview/fragments/monthly_summary.html:167
msgid "Total"
msgstr ""
@@ -2924,6 +2960,63 @@ msgstr ""
msgid "No recent transactions"
msgstr ""
#: templates/insights/fragments/month_by_month.html:86
#: templates/insights/fragments/year_by_year.html:54
msgid "Tag"
msgstr ""
#: templates/insights/fragments/month_by_month.html:94
msgid "Jan"
msgstr ""
#: templates/insights/fragments/month_by_month.html:95
msgid "Feb"
msgstr ""
#: templates/insights/fragments/month_by_month.html:96
msgid "Mar"
msgstr ""
#: templates/insights/fragments/month_by_month.html:97
msgid "Apr"
msgstr ""
#: templates/insights/fragments/month_by_month.html:98
msgid "May"
msgstr ""
#: templates/insights/fragments/month_by_month.html:99
msgid "Jun"
msgstr ""
#: templates/insights/fragments/month_by_month.html:100
msgid "Jul"
msgstr ""
#: templates/insights/fragments/month_by_month.html:101
msgid "Aug"
msgstr ""
#: templates/insights/fragments/month_by_month.html:102
msgid "Sep"
msgstr ""
#: templates/insights/fragments/month_by_month.html:103
msgid "Oct"
msgstr ""
#: templates/insights/fragments/month_by_month.html:104
msgid "Nov"
msgstr ""
#: templates/insights/fragments/month_by_month.html:105
msgid "Dec"
msgstr ""
#: templates/insights/fragments/month_by_month.html:248
msgid "No transactions for this year"
msgstr ""
#: templates/insights/fragments/sankey.html:100
msgid "From"
msgstr ""
@@ -2932,6 +3025,10 @@ msgstr ""
msgid "Percentage"
msgstr ""
#: templates/insights/fragments/year_by_year.html:202
msgid "No transactions"
msgstr ""
#: templates/insights/pages/index.html:37
msgid "Month"
msgstr ""
@@ -2981,6 +3078,14 @@ msgstr ""
msgid "Emergency Fund"
msgstr ""
#: templates/insights/pages/index.html:127
msgid "Year by Year"
msgstr ""
#: templates/insights/pages/index.html:132
msgid "Month by Month"
msgstr ""
#: templates/installment_plans/fragments/add.html:5
msgid "Add installment plan"
msgstr ""
@@ -3052,35 +3157,40 @@ msgstr ""
msgid "Item"
msgstr ""
#: templates/monthly_overview/fragments/list.html:32
#: templates/monthly_overview/fragments/list.html:15
#: templates/transactions/fragments/list_all.html:15
msgid "late"
msgstr ""
#: templates/monthly_overview/fragments/list.html:58
msgid "No transactions this month"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:6
#: templates/monthly_overview/fragments/monthly_summary.html:7
msgid "Daily Spending Allowance"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:6
#: templates/monthly_overview/fragments/monthly_summary.html:7
msgid "This is the final total divided by the remaining days in the month"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:42
#: templates/monthly_overview/fragments/monthly_summary.html:105
#: templates/monthly_overview/fragments/monthly_summary.html:168
#: templates/monthly_overview/fragments/monthly_summary.html:44
#: templates/monthly_overview/fragments/monthly_summary.html:107
#: templates/monthly_overview/fragments/monthly_summary.html:170
msgid "current"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:71
#: templates/monthly_overview/fragments/monthly_summary.html:134
#: templates/monthly_overview/fragments/monthly_summary.html:197
#: templates/monthly_overview/fragments/monthly_summary.html:73
#: templates/monthly_overview/fragments/monthly_summary.html:136
#: templates/monthly_overview/fragments/monthly_summary.html:199
msgid "projected"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:102
#: templates/monthly_overview/fragments/monthly_summary.html:104
msgid "Expenses"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:255
#: templates/monthly_overview/fragments/monthly_summary.html:257
msgid "Distribution"
msgstr ""
@@ -3088,27 +3198,27 @@ msgstr ""
msgid "Summary"
msgstr ""
#: templates/monthly_overview/pages/overview.html:96
#: templates/monthly_overview/pages/overview.html:150
#: templates/monthly_overview/pages/overview.html:99
#: templates/monthly_overview/pages/overview.html:254
#: templates/transactions/pages/transactions.html:48
#: templates/transactions/pages/transactions.html:103
#: templates/transactions/pages/transactions.html:204
msgid "Oldest first"
msgstr ""
#: templates/monthly_overview/pages/overview.html:97
#: templates/monthly_overview/pages/overview.html:159
#: templates/monthly_overview/pages/overview.html:100
#: templates/monthly_overview/pages/overview.html:263
#: templates/transactions/pages/transactions.html:49
#: templates/transactions/pages/transactions.html:112
#: templates/transactions/pages/transactions.html:213
msgid "Newest first"
msgstr ""
#: templates/monthly_overview/pages/overview.html:106
#: templates/monthly_overview/pages/overview.html:109
#: templates/transactions/pages/transactions.html:58
msgid "Filter transactions"
msgstr ""
#: templates/monthly_overview/pages/overview.html:131
#: templates/transactions/pages/transactions.html:84
#: templates/monthly_overview/pages/overview.html:235
#: templates/transactions/pages/transactions.html:185
msgid "Order by"
msgstr ""
@@ -3349,7 +3459,7 @@ msgstr ""
msgid "transactions"
msgstr ""
#: templates/transactions/fragments/list_all.html:32
#: templates/transactions/fragments/list_all.html:58
msgid "No transactions found"
msgstr ""

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-12-20 02:59+0000\n"
"PO-Revision-Date: 2025-12-15 05:24+0000\n"
"POT-Creation-Date: 2026-01-10 20:50+0000\n"
"PO-Revision-Date: 2026-01-11 13:24+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.14.3\n"
"X-Generator: Weblate 5.15.1\n"
#: apps/accounts/forms.py:24
msgid "Group name"
@@ -67,24 +67,30 @@ msgstr "Nieuw saldo"
#: apps/transactions/forms.py:419 apps/transactions/forms.py:516
#: apps/transactions/forms.py:523 apps/transactions/forms.py:707
#: apps/transactions/forms.py:948 apps/transactions/models.py:322
#: apps/transactions/models.py:574 apps/transactions/models.py:774
#: apps/transactions/models.py:1022
#: apps/transactions/models.py:578 apps/transactions/models.py:778
#: apps/transactions/models.py:1026
#: templates/insights/fragments/category_overview/index.html:86
#: templates/insights/fragments/category_overview/index.html:542
#: templates/insights/fragments/month_by_month.html:84
#: templates/insights/fragments/year_by_year.html:52
msgid "Category"
msgstr "Categorie"
#: apps/accounts/forms.py:132 apps/dca/forms.py:95 apps/dca/forms.py:103
#: apps/export_app/forms.py:43 apps/export_app/forms.py:132
#: apps/rules/forms.py:184 apps/rules/forms.py:194 apps/rules/models.py:45
#: apps/rules/models.py:315 apps/transactions/filters.py:68
#: apps/rules/models.py:315 apps/transactions/filters.py:73
#: apps/transactions/forms.py:51 apps/transactions/forms.py:259
#: apps/transactions/forms.py:427 apps/transactions/forms.py:532
#: apps/transactions/forms.py:540 apps/transactions/forms.py:700
#: apps/transactions/forms.py:941 apps/transactions/models.py:328
#: apps/transactions/models.py:576 apps/transactions/models.py:778
#: apps/transactions/models.py:1028 templates/includes/sidebar.html:150
#: apps/transactions/models.py:580 apps/transactions/models.py:782
#: apps/transactions/models.py:1032 templates/includes/sidebar.html:150
#: templates/insights/fragments/category_overview/index.html:40
#: templates/insights/fragments/month_by_month.html:29
#: templates/insights/fragments/month_by_month.html:32
#: templates/insights/fragments/year_by_year.html:24
#: templates/insights/fragments/year_by_year.html:27
#: templates/tags/fragments/list.html:9 templates/tags/pages/index.html:4
msgid "Tags"
msgstr "Labels"
@@ -92,7 +98,7 @@ msgstr "Labels"
#: apps/accounts/models.py:12 apps/accounts/models.py:29 apps/dca/models.py:13
#: apps/import_app/models.py:14 apps/rules/models.py:13
#: apps/transactions/models.py:214 apps/transactions/models.py:239
#: apps/transactions/models.py:263 apps/transactions/models.py:990
#: apps/transactions/models.py:263 apps/transactions/models.py:994
#: templates/account_groups/fragments/list.html:22
#: templates/accounts/fragments/list.html:22
#: templates/categories/fragments/table.html:17
@@ -165,8 +171,8 @@ msgstr ""
#: apps/transactions/forms.py:63 apps/transactions/forms.py:271
#: apps/transactions/forms.py:386 apps/transactions/forms.py:692
#: apps/transactions/forms.py:933 apps/transactions/models.py:294
#: apps/transactions/models.py:534 apps/transactions/models.py:756
#: apps/transactions/models.py:996
#: apps/transactions/models.py:538 apps/transactions/models.py:760
#: apps/transactions/models.py:1000
#: templates/installment_plans/fragments/table.html:17
#: templates/quick_transactions/fragments/list.html:14
#: templates/recurring_transactions/fragments/table.html:19
@@ -175,11 +181,11 @@ msgid "Account"
msgstr "Rekening"
#: apps/accounts/models.py:76 apps/export_app/forms.py:19
#: apps/export_app/forms.py:129 apps/transactions/filters.py:52
#: apps/export_app/forms.py:129 apps/transactions/filters.py:57
#: templates/accounts/fragments/list.html:9
#: templates/accounts/pages/index.html:4 templates/includes/sidebar.html:162
#: templates/includes/sidebar.html:164
#: templates/monthly_overview/pages/overview.html:75
#: templates/monthly_overview/pages/overview.html:77
#: templates/transactions/pages/transactions.html:26
msgid "Accounts"
msgstr "Rekeningen"
@@ -195,8 +201,8 @@ msgstr "Rekeninggroep succesvol toegevoegd"
#: apps/accounts/views/account_groups.py:69
#: apps/accounts/views/account_groups.py:152 apps/accounts/views/accounts.py:68
#: apps/accounts/views/accounts.py:106 apps/dca/views.py:63
#: apps/dca/views.py:146 apps/rules/views.py:118 apps/rules/views.py:228
#: apps/accounts/views/accounts.py:106 apps/dca/views.py:62
#: apps/dca/views.py:145 apps/rules/views.py:118 apps/rules/views.py:228
#: apps/transactions/views/categories.py:91
#: apps/transactions/views/categories.py:129
#: apps/transactions/views/entities.py:91
@@ -210,7 +216,7 @@ msgid "Account Group updated successfully"
msgstr "Rekeninggroep succesvol bijgewerkt"
#: apps/accounts/views/account_groups.py:111
#: apps/accounts/views/accounts.py:145 apps/dca/views.py:105
#: apps/accounts/views/accounts.py:145 apps/dca/views.py:104
#: apps/rules/views.py:185 apps/transactions/views/categories.py:168
#: apps/transactions/views/entities.py:130 apps/transactions/views/tags.py:130
msgid "Item no longer shared with you"
@@ -221,14 +227,14 @@ msgid "Account Group deleted successfully"
msgstr "Rekeninggroep succesvol verwijderd"
#: apps/accounts/views/account_groups.py:135
#: apps/accounts/views/accounts.py:189 apps/dca/views.py:129
#: apps/accounts/views/accounts.py:189 apps/dca/views.py:128
#: apps/rules/views.py:210 apps/transactions/views/categories.py:192
#: apps/transactions/views/entities.py:154 apps/transactions/views/tags.py:154
msgid "Ownership taken successfully"
msgstr "Eigendom succesvol genomen"
#: apps/accounts/views/account_groups.py:165
#: apps/accounts/views/accounts.py:119 apps/dca/views.py:159
#: apps/accounts/views/accounts.py:119 apps/dca/views.py:158
#: apps/rules/views.py:241 apps/transactions/views/categories.py:142
#: apps/transactions/views/entities.py:184 apps/transactions/views/tags.py:184
msgid "Configuration saved successfully"
@@ -366,7 +372,7 @@ msgid "Public"
msgstr "Openbaar"
#: apps/common/templatetags/natural.py:20
#: templates/monthly_overview/fragments/monthly_summary.html:9
#: templates/monthly_overview/fragments/monthly_summary.html:10
msgid "today"
msgstr "vandaag"
@@ -464,10 +470,10 @@ msgstr "Verwijder"
#: apps/common/widgets/tom_select.py:15
#: templates/mini_tools/unit_price_calculator.html:180
#: templates/monthly_overview/pages/overview.html:171
#: templates/monthly_overview/pages/overview.html:183
#: templates/transactions/pages/transactions.html:124
#: templates/transactions/pages/transactions.html:136
#: templates/monthly_overview/pages/overview.html:275
#: templates/monthly_overview/pages/overview.html:287
#: templates/transactions/pages/transactions.html:225
#: templates/transactions/pages/transactions.html:237
msgid "Clear"
msgstr "Leegmaken"
@@ -506,11 +512,11 @@ msgid "Decimal Places"
msgstr "Cijfers na de komma"
#: apps/currencies/models.py:45 apps/export_app/forms.py:25
#: apps/export_app/forms.py:130 apps/transactions/filters.py:59
#: apps/export_app/forms.py:130 apps/transactions/filters.py:64
#: templates/currencies/fragments/list.html:9
#: templates/currencies/pages/index.html:4 templates/includes/sidebar.html:176
#: templates/includes/sidebar.html:178
#: templates/monthly_overview/pages/overview.html:62
#: templates/monthly_overview/pages/overview.html:63
#: templates/transactions/pages/transactions.html:12
msgid "Currencies"
msgstr "Munteenheden"
@@ -571,9 +577,9 @@ msgstr "Dienstnaam"
msgid "Service Type"
msgstr "Soort Dienst"
#: apps/currencies/models.py:118 apps/transactions/models.py:218
#: apps/transactions/models.py:242 apps/transactions/models.py:266
#: templates/categories/fragments/list.html:16
#: apps/currencies/models.py:118 apps/transactions/filters.py:27
#: apps/transactions/models.py:218 apps/transactions/models.py:242
#: apps/transactions/models.py:266 templates/categories/fragments/list.html:16
#: templates/entities/fragments/list.html:16
#: templates/installment_plans/fragments/list.html:16
#: templates/recurring_transactions/fragments/list.html:16
@@ -601,11 +607,11 @@ msgstr "Interval"
msgid "Last Successful Fetch"
msgstr "Laatste Succesvolle Ophaling"
#: apps/currencies/models.py:141
#: apps/currencies/models.py:143
msgid "Target Currencies"
msgstr "Doel Munteenheden"
#: apps/currencies/models.py:143
#: apps/currencies/models.py:145
msgid ""
"Select currencies to fetch exchange rates for. Rates will be fetched for "
"each currency against their set exchange currency."
@@ -613,11 +619,11 @@ msgstr ""
"Selecteer munteenheden om wisselkoersen voor op te halen. De koersen worden "
"voor elke munteenheid opgehaald ten opzichte van de ingestelde wisselkoers."
#: apps/currencies/models.py:151
#: apps/currencies/models.py:153
msgid "Target Accounts"
msgstr "Naar rekeningen"
#: apps/currencies/models.py:153
#: apps/currencies/models.py:155
msgid ""
"Select accounts to fetch exchange rates for. Rates will be fetched for each "
"account's currency against their set exchange currency."
@@ -626,33 +632,33 @@ msgstr ""
"opgehaald voor de munteenheid van elke rekening ten opzichte van de "
"ingestelde wisselkoers."
#: apps/currencies/models.py:160
#: apps/currencies/models.py:162
msgid "Single exchange rate"
msgstr "Enkele Wisselkoers"
#: apps/currencies/models.py:163
#: apps/currencies/models.py:165
msgid "Create one exchange rate and keep updating it. Avoids database clutter."
msgstr ""
"Maak één wisselkoers aan en houd deze bijgewerkt. Voorkomt een overvolle "
"database."
#: apps/currencies/models.py:168
#: apps/currencies/models.py:170
msgid "Exchange Rate Service"
msgstr "Wisselkoersdienst"
#: apps/currencies/models.py:169
#: apps/currencies/models.py:171
msgid "Exchange Rate Services"
msgstr "Wisselkoersdiensten"
#: apps/currencies/models.py:221
#: apps/currencies/models.py:223
msgid "'Every X hours' interval type requires a positive integer."
msgstr "Voor het intervaltype Elke X uur is een positief geheel getal nodig."
#: apps/currencies/models.py:230
#: apps/currencies/models.py:232
msgid "'Every X hours' interval must be between 1 and 24."
msgstr "Het interval Elke X uur moet tussen 1 en 24 liggen."
#: apps/currencies/models.py:244
#: apps/currencies/models.py:246
msgid ""
"Invalid hour format. Use comma-separated hours (0-23) and/or ranges (e.g., "
"'1-5,8,10-12')."
@@ -660,7 +666,7 @@ msgstr ""
"Ongeldige urennotatie. Gebruik door komma's gescheiden uren (0-23) en/of "
"reeksen (bijv. 1-5,8,10-12)."
#: apps/currencies/models.py:255
#: apps/currencies/models.py:257
msgid ""
"Invalid format. Please check the requirements for your selected interval "
"type."
@@ -762,8 +768,8 @@ msgstr "Betaal Munteenheid"
#: apps/dca/models.py:26 apps/dca/models.py:181 apps/rules/forms.py:180
#: apps/rules/forms.py:196 apps/rules/models.py:43 apps/rules/models.py:295
#: apps/transactions/forms.py:413 apps/transactions/forms.py:560
#: apps/transactions/models.py:318 apps/transactions/models.py:583
#: apps/transactions/models.py:784 apps/transactions/models.py:1018
#: apps/transactions/models.py:318 apps/transactions/models.py:587
#: apps/transactions/models.py:788 apps/transactions/models.py:1022
msgid "Notes"
msgstr "Opmerkingen"
@@ -795,27 +801,27 @@ msgstr "DCA Instap"
msgid "DCA Entries"
msgstr "DCA Idems"
#: apps/dca/views.py:39
#: apps/dca/views.py:38
msgid "DCA Strategy added successfully"
msgstr "Strategie voor DCA succesvol toegevoegd"
#: apps/dca/views.py:76
#: apps/dca/views.py:75
msgid "DCA Strategy updated successfully"
msgstr "Strategie voor DCA succesvol bijgewerkt"
#: apps/dca/views.py:108
#: apps/dca/views.py:107
msgid "DCA strategy deleted successfully"
msgstr "Strategie voor DCA succesvol verwijderd"
#: apps/dca/views.py:238
#: apps/dca/views.py:237
msgid "Entry added successfully"
msgstr "Item succesvol toegevoegd"
#: apps/dca/views.py:265
#: apps/dca/views.py:264
msgid "Entry updated successfully"
msgstr "Invoer succesvol bijgewerkt"
#: apps/dca/views.py:291
#: apps/dca/views.py:290
msgid "Entry deleted successfully"
msgstr "Invoer succesvol verwijderd"
@@ -835,34 +841,42 @@ msgid "Transactions"
msgstr "Verrichtingen"
#: apps/export_app/forms.py:37 apps/export_app/forms.py:131
#: apps/transactions/filters.py:63 templates/categories/fragments/list.html:9
#: apps/transactions/filters.py:68 templates/categories/fragments/list.html:9
#: templates/categories/pages/index.html:4 templates/includes/sidebar.html:144
#: templates/insights/fragments/month_by_month.html:18
#: templates/insights/fragments/month_by_month.html:21
#: templates/insights/fragments/year_by_year.html:13
#: templates/insights/fragments/year_by_year.html:16
msgid "Categories"
msgstr "Categorieën"
#: apps/export_app/forms.py:49 apps/export_app/forms.py:133
#: apps/rules/forms.py:185 apps/rules/forms.py:195 apps/rules/models.py:46
#: apps/rules/models.py:307 apps/transactions/filters.py:73
#: apps/rules/models.py:307 apps/transactions/filters.py:78
#: apps/transactions/forms.py:59 apps/transactions/forms.py:267
#: apps/transactions/forms.py:435 apps/transactions/forms.py:715
#: apps/transactions/forms.py:956 apps/transactions/models.py:277
#: apps/transactions/models.py:333 apps/transactions/models.py:579
#: apps/transactions/models.py:781 apps/transactions/models.py:1033
#: apps/transactions/models.py:333 apps/transactions/models.py:583
#: apps/transactions/models.py:785 apps/transactions/models.py:1037
#: templates/entities/fragments/list.html:9
#: templates/entities/pages/index.html:4 templates/includes/sidebar.html:156
#: templates/insights/fragments/category_overview/index.html:54
#: templates/insights/fragments/month_by_month.html:40
#: templates/insights/fragments/month_by_month.html:43
#: templates/insights/fragments/year_by_year.html:35
#: templates/insights/fragments/year_by_year.html:38
msgid "Entities"
msgstr "Bedrijven"
#: apps/export_app/forms.py:55 apps/export_app/forms.py:137
#: apps/transactions/models.py:821 templates/includes/sidebar.html:110
#: apps/transactions/models.py:825 templates/includes/sidebar.html:110
#: templates/recurring_transactions/fragments/list.html:9
#: templates/recurring_transactions/pages/index.html:4
msgid "Recurring Transactions"
msgstr "Terugkerende Verrichtingen"
#: apps/export_app/forms.py:61 apps/export_app/forms.py:135
#: apps/transactions/models.py:597 templates/includes/sidebar.html:104
#: apps/transactions/models.py:601 templates/includes/sidebar.html:104
#: templates/installment_plans/fragments/list.html:9
#: templates/installment_plans/pages/index.html:4
msgid "Installment Plans"
@@ -1016,10 +1030,12 @@ msgid "Run deleted successfully"
msgstr "Run met succes verwijderd"
#: apps/insights/forms.py:118 apps/insights/utils/sankey.py:36
#: apps/insights/utils/sankey.py:167 apps/transactions/filters.py:186
#: apps/insights/utils/sankey.py:167 apps/transactions/filters.py:203
#: templates/insights/fragments/category_overview/index.html:96
#: templates/insights/fragments/category_overview/index.html:407
#: templates/insights/fragments/category_overview/index.html:436
#: templates/insights/fragments/month_by_month.html:119
#: templates/insights/fragments/year_by_year.html:73
msgid "Uncategorized"
msgstr "Ongecategoriseerd"
@@ -1112,15 +1128,15 @@ msgstr "Operator"
#: apps/rules/forms.py:174 apps/rules/forms.py:188 apps/rules/models.py:36
#: apps/rules/models.py:271 apps/transactions/forms.py:377
#: apps/transactions/models.py:301 apps/transactions/models.py:539
#: apps/transactions/models.py:762 apps/transactions/models.py:1003
#: apps/transactions/models.py:301 apps/transactions/models.py:543
#: apps/transactions/models.py:766 apps/transactions/models.py:1007
msgid "Type"
msgstr "Soort"
#: apps/rules/forms.py:175 apps/rules/forms.py:189 apps/rules/models.py:37
#: apps/rules/models.py:275 apps/transactions/filters.py:22
#: apps/transactions/forms.py:381 apps/transactions/models.py:303
#: apps/transactions/models.py:1005 templates/cotton/transaction/item.html:20
#: apps/transactions/models.py:1009 templates/cotton/transaction/item.html:20
#: templates/cotton/transaction/item.html:31
#: templates/transactions/widgets/paid_toggle_button.html:10
#: templates/transactions/widgets/unselectable_paid_toggle_button.html:13
@@ -1131,14 +1147,14 @@ msgstr "Betaald"
#: apps/rules/models.py:283 apps/transactions/forms.py:71
#: apps/transactions/forms.py:397 apps/transactions/forms.py:547
#: apps/transactions/forms.py:721 apps/transactions/models.py:305
#: apps/transactions/models.py:557 apps/transactions/models.py:786
#: apps/transactions/models.py:561 apps/transactions/models.py:790
msgid "Reference Date"
msgstr "Referentiedatum"
#: apps/rules/forms.py:178 apps/rules/forms.py:192 apps/rules/models.py:41
#: apps/rules/models.py:287 apps/transactions/forms.py:404
#: apps/transactions/models.py:311 apps/transactions/models.py:767
#: apps/transactions/models.py:1011
#: apps/transactions/models.py:311 apps/transactions/models.py:771
#: apps/transactions/models.py:1015
#: templates/insights/fragments/sankey.html:102
#: templates/installment_plans/fragments/table.html:18
#: templates/quick_transactions/fragments/list.html:15
@@ -1149,27 +1165,27 @@ msgstr "Bedrag"
#: apps/rules/forms.py:179 apps/rules/forms.py:193 apps/rules/models.py:14
#: apps/rules/models.py:42 apps/rules/models.py:291
#: apps/transactions/forms.py:408 apps/transactions/forms.py:551
#: apps/transactions/models.py:316 apps/transactions/models.py:541
#: apps/transactions/models.py:770 apps/transactions/models.py:1016
#: apps/transactions/models.py:316 apps/transactions/models.py:545
#: apps/transactions/models.py:774 apps/transactions/models.py:1020
msgid "Description"
msgstr "Beschrijving"
#: apps/rules/forms.py:182 apps/rules/forms.py:198 apps/rules/models.py:47
#: apps/rules/models.py:299 apps/transactions/models.py:355
#: apps/transactions/models.py:1038
#: apps/transactions/models.py:1042
msgid "Internal Note"
msgstr "Interne opmerking"
#: apps/rules/forms.py:183 apps/rules/forms.py:199 apps/rules/models.py:48
#: apps/rules/models.py:303 apps/transactions/models.py:357
#: apps/transactions/models.py:1040
#: apps/transactions/models.py:1044
msgid "Internal ID"
msgstr "Interne ID"
#: apps/rules/forms.py:186 apps/rules/forms.py:200 apps/rules/models.py:40
#: apps/rules/models.py:319 apps/transactions/forms.py:564
#: apps/transactions/models.py:215 apps/transactions/models.py:306
#: apps/transactions/models.py:1006
#: apps/transactions/models.py:1010
msgid "Mute"
msgstr "Dempen"
@@ -1327,53 +1343,65 @@ msgstr "Verrichting Bijwerken Of Maken succesvol verwijderd"
msgid "Projected"
msgstr "Ingepland"
#: apps/transactions/filters.py:40
#: apps/transactions/filters.py:28 templates/categories/fragments/table.html:18
msgid "Muted"
msgstr "Gedempt"
#: apps/transactions/filters.py:45
msgid "Content"
msgstr "Inhoud"
#: apps/transactions/filters.py:46
#: apps/transactions/filters.py:51
msgid "Transaction Type"
msgstr "Soort transactie"
#: apps/transactions/filters.py:84
#: apps/transactions/filters.py:89
msgid "Mute Status"
msgstr "Demp Status"
#: apps/transactions/filters.py:94
msgid "Date from"
msgstr "Datum vanaf"
#: apps/transactions/filters.py:89 apps/transactions/filters.py:99
#: apps/transactions/filters.py:99 apps/transactions/filters.py:109
msgid "Until"
msgstr "Tot"
#: apps/transactions/filters.py:94
#: apps/transactions/filters.py:104
msgid "Reference date from"
msgstr "Referentiedatum vanaf"
#: apps/transactions/filters.py:104
#: apps/transactions/filters.py:114
msgid "Amount min"
msgstr "Minimum bedrag"
#: apps/transactions/filters.py:109
#: apps/transactions/filters.py:119
msgid "Amount max"
msgstr "Maximaal bedrag"
#: apps/transactions/filters.py:185
#: apps/transactions/filters.py:202
msgid "Categorized"
msgstr "Gecategoriseerd"
#: apps/transactions/filters.py:192
#: apps/transactions/filters.py:209
msgid "Tagged"
msgstr "Gelabeld"
#: apps/transactions/filters.py:192
#: apps/transactions/filters.py:209
#: templates/insights/fragments/category_overview/index.html:189
#: templates/insights/fragments/month_by_month.html:121
#: templates/insights/fragments/year_by_year.html:75
msgid "Untagged"
msgstr "Niet gelabeld"
#: apps/transactions/filters.py:198
#: apps/transactions/filters.py:215
msgid "Any entity"
msgstr "Elk bedrijf"
#: apps/transactions/filters.py:199
#: apps/transactions/filters.py:216
#: templates/insights/fragments/category_overview/index.html:282
#: templates/insights/fragments/month_by_month.html:123
#: templates/insights/fragments/year_by_year.html:77
msgid "No entity"
msgstr "Geen bedrijf"
@@ -1467,10 +1495,12 @@ msgstr ""
"nieuwe verrichtingen"
#: apps/transactions/models.py:276
#: templates/insights/fragments/month_by_month.html:88
#: templates/insights/fragments/year_by_year.html:56
msgid "Entity"
msgstr "Bedrijf"
#: apps/transactions/models.py:288 apps/transactions/models.py:983
#: apps/transactions/models.py:288 apps/transactions/models.py:987
#: templates/calendar_view/fragments/list.html:42
#: templates/calendar_view/fragments/list.html:44
#: templates/calendar_view/fragments/list.html:52
@@ -1478,11 +1508,11 @@ msgstr "Bedrijf"
#: templates/cotton/ui/quick_transactions_buttons.html:10
#: templates/cotton/ui/transactions_fab.html:10
#: templates/insights/fragments/category_overview/index.html:87
#: templates/monthly_overview/fragments/monthly_summary.html:39
#: templates/monthly_overview/fragments/monthly_summary.html:41
msgid "Income"
msgstr "Ontvangsten Transactie"
#: apps/transactions/models.py:289 apps/transactions/models.py:984
#: apps/transactions/models.py:289 apps/transactions/models.py:988
#: templates/calendar_view/fragments/list.html:46
#: templates/calendar_view/fragments/list.html:48
#: templates/calendar_view/fragments/list.html:56
@@ -1493,11 +1523,11 @@ msgstr "Ontvangsten Transactie"
msgid "Expense"
msgstr "Uitgave"
#: apps/transactions/models.py:344 apps/transactions/models.py:596
#: apps/transactions/models.py:344 apps/transactions/models.py:600
msgid "Installment Plan"
msgstr "Afbetalingsplan"
#: apps/transactions/models.py:353 apps/transactions/models.py:820
#: apps/transactions/models.py:353 apps/transactions/models.py:824
msgid "Recurring Transaction"
msgstr "Terugkerende verrichting"
@@ -1509,113 +1539,113 @@ msgstr "Verwijderd"
msgid "Deleted At"
msgstr "Verwijderd Op"
#: apps/transactions/models.py:476 templates/tags/fragments/table.html:69
#: apps/transactions/models.py:480 templates/tags/fragments/table.html:69
msgid "No tags"
msgstr "Geen labels"
#: apps/transactions/models.py:478
#: apps/transactions/models.py:482
msgid "No category"
msgstr "Geen categorie"
#: apps/transactions/models.py:480
#: apps/transactions/models.py:484
msgid "No description"
msgstr "Geen Beschrijving"
#: apps/transactions/models.py:528 templates/includes/sidebar.html:57
#: apps/transactions/models.py:532 templates/includes/sidebar.html:57
msgid "Yearly"
msgstr "Jaarlijks"
#: apps/transactions/models.py:529 apps/users/models.py:464
#: apps/transactions/models.py:533 apps/users/models.py:464
#: templates/includes/sidebar.html:51
msgid "Monthly"
msgstr "Maandelijks"
#: apps/transactions/models.py:530
#: apps/transactions/models.py:534
msgid "Weekly"
msgstr "Wekelijks"
#: apps/transactions/models.py:531
#: apps/transactions/models.py:535
msgid "Daily"
msgstr "Dagelijks"
#: apps/transactions/models.py:544
#: apps/transactions/models.py:548
msgid "Number of Installments"
msgstr "Aantal aflossingen"
#: apps/transactions/models.py:549
#: apps/transactions/models.py:553
msgid "Installment Start"
msgstr "Begin afbetaling"
#: apps/transactions/models.py:550
#: apps/transactions/models.py:554
msgid "The installment number to start counting from"
msgstr "Het nummer van de aflevering om mee te beginnen"
#: apps/transactions/models.py:555 apps/transactions/models.py:790
#: apps/transactions/models.py:559 apps/transactions/models.py:794
msgid "Start Date"
msgstr "Startdatum"
#: apps/transactions/models.py:559 apps/transactions/models.py:791
#: apps/transactions/models.py:563 apps/transactions/models.py:795
msgid "End Date"
msgstr "Einddatum"
#: apps/transactions/models.py:564
#: apps/transactions/models.py:568
msgid "Recurrence"
msgstr "Terugkeerpatroon"
#: apps/transactions/models.py:567
#: apps/transactions/models.py:571
msgid "Installment Amount"
msgstr "Termijnbedrag"
#: apps/transactions/models.py:586 apps/transactions/models.py:810
#: apps/transactions/models.py:590 apps/transactions/models.py:814
msgid "Add description to transactions"
msgstr "Beschrijving toevoegen aan verrichting"
#: apps/transactions/models.py:589 apps/transactions/models.py:813
#: apps/transactions/models.py:593 apps/transactions/models.py:817
msgid "Add notes to transactions"
msgstr "Notities toevoegen aan verrichting"
#: apps/transactions/models.py:749
#: apps/transactions/models.py:753
msgid "day(s)"
msgstr "dag(en)"
#: apps/transactions/models.py:750
#: apps/transactions/models.py:754
msgid "week(s)"
msgstr "we(e)k(en)"
#: apps/transactions/models.py:751
#: apps/transactions/models.py:755
msgid "month(s)"
msgstr "maand(en)"
#: apps/transactions/models.py:752
#: apps/transactions/models.py:756
msgid "year(s)"
msgstr "ja(a)r(en)"
#: apps/transactions/models.py:754
#: apps/transactions/models.py:758
#: templates/recurring_transactions/fragments/list.html:18
msgid "Paused"
msgstr "Gepauzeerd"
#: apps/transactions/models.py:793
#: apps/transactions/models.py:797
msgid "Recurrence Type"
msgstr "Type Terugkeerpatroon"
#: apps/transactions/models.py:796
#: apps/transactions/models.py:800
msgid "Recurrence Interval"
msgstr "Terugkeer Interval"
#: apps/transactions/models.py:799
#: apps/transactions/models.py:803
msgid "Keep at most"
msgstr "Bewaar maximaal"
#: apps/transactions/models.py:803
#: apps/transactions/models.py:807
msgid "Last Generated Date"
msgstr "Laatste Gegenereerde Datum"
#: apps/transactions/models.py:806
#: apps/transactions/models.py:810
msgid "Last Generated Reference Date"
msgstr "Laatste Gegenereerde Referentiedatum"
#: apps/transactions/models.py:1050
#: apps/transactions/models.py:1054
#: apps/transactions/views/quick_transactions.py:178
#: apps/transactions/views/quick_transactions.py:187
#: apps/transactions/views/quick_transactions.py:189
@@ -1624,7 +1654,7 @@ msgstr "Laatste Gegenereerde Referentiedatum"
msgid "Quick Transaction"
msgstr "Snelle verrichting"
#: apps/transactions/models.py:1051 templates/includes/sidebar.html:98
#: apps/transactions/models.py:1055 templates/includes/sidebar.html:98
#: templates/quick_transactions/pages/index.html:5
#: templates/quick_transactions/pages/index.html:15
msgid "Quick Transactions"
@@ -1730,7 +1760,7 @@ msgstr "Item succesvol verwijderd"
#: apps/transactions/views/quick_transactions.py:156
#: apps/transactions/views/transactions.py:53
#: apps/transactions/views/transactions.py:228
#: apps/transactions/views/transactions.py:238
msgid "Transaction added successfully"
msgstr "Verrichting succesvol toegevoegd"
@@ -1770,30 +1800,30 @@ msgstr "Label succesvol bijgewerkt"
msgid "Tag deleted successfully"
msgstr "Label succesvol verwijderd"
#: apps/transactions/views/transactions.py:252
#: apps/transactions/views/transactions.py:262
msgid "Transaction updated successfully"
msgstr "Verrichting succesvol bijgewerkt"
#: apps/transactions/views/transactions.py:303
#: apps/transactions/views/transactions.py:313
#, python-format
msgid "%(count)s transaction updated successfully"
msgid_plural "%(count)s transactions updated successfully"
msgstr[0] "%(count)s verrichting succesvol bijgewerkt"
msgstr[1] "%(count)s verrichtingen succesvol bijgewerkt"
#: apps/transactions/views/transactions.py:339
#: apps/transactions/views/transactions.py:349
msgid "Transaction duplicated successfully"
msgstr "Verrichting succesvol gedupliceerd"
#: apps/transactions/views/transactions.py:381
#: apps/transactions/views/transactions.py:391
msgid "Transaction deleted successfully"
msgstr "Verrichting succesvol verwijderd"
#: apps/transactions/views/transactions.py:399
#: apps/transactions/views/transactions.py:409
msgid "Transaction restored successfully"
msgstr "Verrichting succesvol hersteld"
#: apps/transactions/views/transactions.py:425
#: apps/transactions/views/transactions.py:435
msgid "Transfer added successfully"
msgstr "Transactie succesvol toegevoegd"
@@ -1835,10 +1865,10 @@ msgid "This account is deactivated"
msgstr "Deze gebruiker is gedeactiveerd"
#: apps/users/forms.py:62 apps/users/forms.py:75 apps/users/forms.py:97
#: templates/monthly_overview/pages/overview.html:95
#: templates/monthly_overview/pages/overview.html:141
#: templates/monthly_overview/pages/overview.html:98
#: templates/monthly_overview/pages/overview.html:245
#: templates/transactions/pages/transactions.html:47
#: templates/transactions/pages/transactions.html:94
#: templates/transactions/pages/transactions.html:195
msgid "Default"
msgstr "Standaard"
@@ -2265,10 +2295,6 @@ msgstr "Categorie toevoegen"
msgid "Edit category"
msgstr "Categorie bewerken"
#: templates/categories/fragments/table.html:18
msgid "Muted"
msgstr "Gedempt"
#: templates/categories/fragments/table.html:73
#: templates/insights/fragments/category_overview/index.html:552
msgid "No categories"
@@ -2287,9 +2313,9 @@ msgstr "Sluiten"
#: templates/cotton/config/search.html:6
#: templates/import_app/fragments/profiles/list_presets.html:13
#: templates/monthly_overview/pages/overview.html:115
#: templates/monthly_overview/pages/overview.html:219
#: templates/rules/fragments/transaction_rule/dry_run/visual.html:57
#: templates/transactions/pages/transactions.html:67
#: templates/transactions/pages/transactions.html:168
msgid "Search"
msgstr "Zoeken"
@@ -2517,8 +2543,8 @@ msgid "No entries for this DCA"
msgstr "Geen idems in deze DCA"
#: templates/dca/fragments/strategy/details.html:120
#: templates/monthly_overview/fragments/list.html:33
#: templates/transactions/fragments/list_all.html:33
#: templates/monthly_overview/fragments/list.html:59
#: templates/transactions/fragments/list_all.html:59
msgid "Try adding one"
msgstr "Probeer er een toe te voegen"
@@ -2643,7 +2669,7 @@ msgstr "Geen wisselkoersen"
#: templates/exchange_rates/fragments/table.html:56
#: templates/exchange_rates_services/fragments/table.html:57
#: templates/transactions/fragments/list_all.html:43
#: templates/transactions/fragments/list_all.html:70
msgid "Page navigation"
msgstr "Paginanavigatie"
@@ -2663,15 +2689,22 @@ msgstr "Gericht op"
msgid "Last fetch"
msgstr "Laatst opgehaald"
#: templates/exchange_rates_services/fragments/list.html:61
#: templates/exchange_rates_services/fragments/list.html:62
#, python-format
msgid "%(counter)s consecutive failure"
msgid_plural "%(counter)s consecutive failures"
msgstr[0] "%(counter)s opeenvolgende mislukking"
msgstr[1] "%(counter)s opeenvolgende mislukkingen"
#: templates/exchange_rates_services/fragments/list.html:69
msgid "currencies"
msgstr "munteenheden"
#: templates/exchange_rates_services/fragments/list.html:61
#: templates/exchange_rates_services/fragments/list.html:69
msgid "accounts"
msgstr "rekeningen"
#: templates/exchange_rates_services/fragments/list.html:69
#: templates/exchange_rates_services/fragments/list.html:77
msgid "No services configured"
msgstr "Geen diensten ingesteld"
@@ -2928,7 +2961,11 @@ msgid "Final total"
msgstr "Eindtotaal"
#: templates/insights/fragments/category_overview/index.html:89
#: templates/monthly_overview/fragments/monthly_summary.html:165
#: templates/insights/fragments/month_by_month.html:91
#: templates/insights/fragments/month_by_month.html:186
#: templates/insights/fragments/year_by_year.html:59
#: templates/insights/fragments/year_by_year.html:140
#: templates/monthly_overview/fragments/monthly_summary.html:167
msgid "Total"
msgstr "Totaal"
@@ -2972,6 +3009,63 @@ msgstr "Geen betalingsachterstanden"
msgid "No recent transactions"
msgstr "Geen recente betalingen"
#: templates/insights/fragments/month_by_month.html:86
#: templates/insights/fragments/year_by_year.html:54
msgid "Tag"
msgstr "Label"
#: templates/insights/fragments/month_by_month.html:94
msgid "Jan"
msgstr "Jan"
#: templates/insights/fragments/month_by_month.html:95
msgid "Feb"
msgstr "Feb"
#: templates/insights/fragments/month_by_month.html:96
msgid "Mar"
msgstr "Mrt"
#: templates/insights/fragments/month_by_month.html:97
msgid "Apr"
msgstr "Apr"
#: templates/insights/fragments/month_by_month.html:98
msgid "May"
msgstr "Mei"
#: templates/insights/fragments/month_by_month.html:99
msgid "Jun"
msgstr "Jun"
#: templates/insights/fragments/month_by_month.html:100
msgid "Jul"
msgstr "Jul"
#: templates/insights/fragments/month_by_month.html:101
msgid "Aug"
msgstr "Aug"
#: templates/insights/fragments/month_by_month.html:102
msgid "Sep"
msgstr "Sep"
#: templates/insights/fragments/month_by_month.html:103
msgid "Oct"
msgstr "Okt"
#: templates/insights/fragments/month_by_month.html:104
msgid "Nov"
msgstr "Nov"
#: templates/insights/fragments/month_by_month.html:105
msgid "Dec"
msgstr "Dec"
#: templates/insights/fragments/month_by_month.html:248
msgid "No transactions for this year"
msgstr "Geen verrichtingen voor dit jaar"
#: templates/insights/fragments/sankey.html:100
msgid "From"
msgstr "Van"
@@ -2980,6 +3074,10 @@ msgstr "Van"
msgid "Percentage"
msgstr "Percentage"
#: templates/insights/fragments/year_by_year.html:202
msgid "No transactions"
msgstr "Geen verrichtingen"
#: templates/insights/pages/index.html:37
msgid "Month"
msgstr "Maand"
@@ -3029,6 +3127,14 @@ msgstr "Laatste Verrichtingen"
msgid "Emergency Fund"
msgstr "Noodfonds"
#: templates/insights/pages/index.html:127
msgid "Year by Year"
msgstr "Jaar bij Jaar"
#: templates/insights/pages/index.html:132
msgid "Month by Month"
msgstr "Maand bij Maand"
#: templates/installment_plans/fragments/add.html:5
msgid "Add installment plan"
msgstr "Afbetalingsplan toevoegen"
@@ -3103,35 +3209,40 @@ msgstr "Eenheidsprijs"
msgid "Item"
msgstr "Artikel"
#: templates/monthly_overview/fragments/list.html:32
#: templates/monthly_overview/fragments/list.html:15
#: templates/transactions/fragments/list_all.html:15
msgid "late"
msgstr "laat"
#: templates/monthly_overview/fragments/list.html:58
msgid "No transactions this month"
msgstr "Geen verrichtingen deze maand"
#: templates/monthly_overview/fragments/monthly_summary.html:6
#: templates/monthly_overview/fragments/monthly_summary.html:7
msgid "Daily Spending Allowance"
msgstr "Dagelijks Toegestane Besteding"
#: templates/monthly_overview/fragments/monthly_summary.html:6
#: templates/monthly_overview/fragments/monthly_summary.html:7
msgid "This is the final total divided by the remaining days in the month"
msgstr "Dit is het eindtotaal gedeeld door de resterende dagen in de maand"
#: templates/monthly_overview/fragments/monthly_summary.html:42
#: templates/monthly_overview/fragments/monthly_summary.html:105
#: templates/monthly_overview/fragments/monthly_summary.html:168
#: templates/monthly_overview/fragments/monthly_summary.html:44
#: templates/monthly_overview/fragments/monthly_summary.html:107
#: templates/monthly_overview/fragments/monthly_summary.html:170
msgid "current"
msgstr "actueel"
#: templates/monthly_overview/fragments/monthly_summary.html:71
#: templates/monthly_overview/fragments/monthly_summary.html:134
#: templates/monthly_overview/fragments/monthly_summary.html:197
#: templates/monthly_overview/fragments/monthly_summary.html:73
#: templates/monthly_overview/fragments/monthly_summary.html:136
#: templates/monthly_overview/fragments/monthly_summary.html:199
msgid "projected"
msgstr "verwacht"
#: templates/monthly_overview/fragments/monthly_summary.html:102
#: templates/monthly_overview/fragments/monthly_summary.html:104
msgid "Expenses"
msgstr "Uitgaven"
#: templates/monthly_overview/fragments/monthly_summary.html:255
#: templates/monthly_overview/fragments/monthly_summary.html:257
msgid "Distribution"
msgstr "Verdeling"
@@ -3139,27 +3250,27 @@ msgstr "Verdeling"
msgid "Summary"
msgstr "Samenvatting"
#: templates/monthly_overview/pages/overview.html:96
#: templates/monthly_overview/pages/overview.html:150
#: templates/monthly_overview/pages/overview.html:99
#: templates/monthly_overview/pages/overview.html:254
#: templates/transactions/pages/transactions.html:48
#: templates/transactions/pages/transactions.html:103
#: templates/transactions/pages/transactions.html:204
msgid "Oldest first"
msgstr "Oudste eerst"
#: templates/monthly_overview/pages/overview.html:97
#: templates/monthly_overview/pages/overview.html:159
#: templates/monthly_overview/pages/overview.html:100
#: templates/monthly_overview/pages/overview.html:263
#: templates/transactions/pages/transactions.html:49
#: templates/transactions/pages/transactions.html:112
#: templates/transactions/pages/transactions.html:213
msgid "Newest first"
msgstr "Nieuwste eerst"
#: templates/monthly_overview/pages/overview.html:106
#: templates/monthly_overview/pages/overview.html:109
#: templates/transactions/pages/transactions.html:58
msgid "Filter transactions"
msgstr "Filter verrichtingen"
#: templates/monthly_overview/pages/overview.html:131
#: templates/transactions/pages/transactions.html:84
#: templates/monthly_overview/pages/overview.html:235
#: templates/transactions/pages/transactions.html:185
msgid "Order by"
msgstr "Sorteer op"
@@ -3406,7 +3517,7 @@ msgstr "Bewerking"
msgid "transactions"
msgstr "verrichtingen"
#: templates/transactions/fragments/list_all.html:32
#: templates/transactions/fragments/list_all.html:58
msgid "No transactions found"
msgstr "Geen Verrichtingen gevonden"

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-12-20 02:59+0000\n"
"POT-Creation-Date: 2026-01-10 20:50+0000\n"
"PO-Revision-Date: 2025-11-08 12:20+0000\n"
"Last-Translator: Marcin Kisielewski <kisielewski.mar@gmail.com>\n"
"Language-Team: Polish <https://translations.herculino.com/projects/wygiwyh/"
@@ -68,24 +68,30 @@ msgstr "Nowe saldo"
#: apps/transactions/forms.py:419 apps/transactions/forms.py:516
#: apps/transactions/forms.py:523 apps/transactions/forms.py:707
#: apps/transactions/forms.py:948 apps/transactions/models.py:322
#: apps/transactions/models.py:574 apps/transactions/models.py:774
#: apps/transactions/models.py:1022
#: apps/transactions/models.py:578 apps/transactions/models.py:778
#: apps/transactions/models.py:1026
#: templates/insights/fragments/category_overview/index.html:86
#: templates/insights/fragments/category_overview/index.html:542
#: templates/insights/fragments/month_by_month.html:84
#: templates/insights/fragments/year_by_year.html:52
msgid "Category"
msgstr "Kategoria"
#: apps/accounts/forms.py:132 apps/dca/forms.py:95 apps/dca/forms.py:103
#: apps/export_app/forms.py:43 apps/export_app/forms.py:132
#: apps/rules/forms.py:184 apps/rules/forms.py:194 apps/rules/models.py:45
#: apps/rules/models.py:315 apps/transactions/filters.py:68
#: apps/rules/models.py:315 apps/transactions/filters.py:73
#: apps/transactions/forms.py:51 apps/transactions/forms.py:259
#: apps/transactions/forms.py:427 apps/transactions/forms.py:532
#: apps/transactions/forms.py:540 apps/transactions/forms.py:700
#: apps/transactions/forms.py:941 apps/transactions/models.py:328
#: apps/transactions/models.py:576 apps/transactions/models.py:778
#: apps/transactions/models.py:1028 templates/includes/sidebar.html:150
#: apps/transactions/models.py:580 apps/transactions/models.py:782
#: apps/transactions/models.py:1032 templates/includes/sidebar.html:150
#: templates/insights/fragments/category_overview/index.html:40
#: templates/insights/fragments/month_by_month.html:29
#: templates/insights/fragments/month_by_month.html:32
#: templates/insights/fragments/year_by_year.html:24
#: templates/insights/fragments/year_by_year.html:27
#: templates/tags/fragments/list.html:9 templates/tags/pages/index.html:4
msgid "Tags"
msgstr "Tagi"
@@ -93,7 +99,7 @@ msgstr "Tagi"
#: apps/accounts/models.py:12 apps/accounts/models.py:29 apps/dca/models.py:13
#: apps/import_app/models.py:14 apps/rules/models.py:13
#: apps/transactions/models.py:214 apps/transactions/models.py:239
#: apps/transactions/models.py:263 apps/transactions/models.py:990
#: apps/transactions/models.py:263 apps/transactions/models.py:994
#: templates/account_groups/fragments/list.html:22
#: templates/accounts/fragments/list.html:22
#: templates/categories/fragments/table.html:17
@@ -162,8 +168,8 @@ msgstr ""
#: apps/transactions/forms.py:63 apps/transactions/forms.py:271
#: apps/transactions/forms.py:386 apps/transactions/forms.py:692
#: apps/transactions/forms.py:933 apps/transactions/models.py:294
#: apps/transactions/models.py:534 apps/transactions/models.py:756
#: apps/transactions/models.py:996
#: apps/transactions/models.py:538 apps/transactions/models.py:760
#: apps/transactions/models.py:1000
#: templates/installment_plans/fragments/table.html:17
#: templates/quick_transactions/fragments/list.html:14
#: templates/recurring_transactions/fragments/table.html:19
@@ -172,11 +178,11 @@ msgid "Account"
msgstr ""
#: apps/accounts/models.py:76 apps/export_app/forms.py:19
#: apps/export_app/forms.py:129 apps/transactions/filters.py:52
#: apps/export_app/forms.py:129 apps/transactions/filters.py:57
#: templates/accounts/fragments/list.html:9
#: templates/accounts/pages/index.html:4 templates/includes/sidebar.html:162
#: templates/includes/sidebar.html:164
#: templates/monthly_overview/pages/overview.html:75
#: templates/monthly_overview/pages/overview.html:77
#: templates/transactions/pages/transactions.html:26
msgid "Accounts"
msgstr ""
@@ -191,8 +197,8 @@ msgstr ""
#: apps/accounts/views/account_groups.py:69
#: apps/accounts/views/account_groups.py:152 apps/accounts/views/accounts.py:68
#: apps/accounts/views/accounts.py:106 apps/dca/views.py:63
#: apps/dca/views.py:146 apps/rules/views.py:118 apps/rules/views.py:228
#: apps/accounts/views/accounts.py:106 apps/dca/views.py:62
#: apps/dca/views.py:145 apps/rules/views.py:118 apps/rules/views.py:228
#: apps/transactions/views/categories.py:91
#: apps/transactions/views/categories.py:129
#: apps/transactions/views/entities.py:91
@@ -206,7 +212,7 @@ msgid "Account Group updated successfully"
msgstr ""
#: apps/accounts/views/account_groups.py:111
#: apps/accounts/views/accounts.py:145 apps/dca/views.py:105
#: apps/accounts/views/accounts.py:145 apps/dca/views.py:104
#: apps/rules/views.py:185 apps/transactions/views/categories.py:168
#: apps/transactions/views/entities.py:130 apps/transactions/views/tags.py:130
msgid "Item no longer shared with you"
@@ -217,14 +223,14 @@ msgid "Account Group deleted successfully"
msgstr ""
#: apps/accounts/views/account_groups.py:135
#: apps/accounts/views/accounts.py:189 apps/dca/views.py:129
#: apps/accounts/views/accounts.py:189 apps/dca/views.py:128
#: apps/rules/views.py:210 apps/transactions/views/categories.py:192
#: apps/transactions/views/entities.py:154 apps/transactions/views/tags.py:154
msgid "Ownership taken successfully"
msgstr ""
#: apps/accounts/views/account_groups.py:165
#: apps/accounts/views/accounts.py:119 apps/dca/views.py:159
#: apps/accounts/views/accounts.py:119 apps/dca/views.py:158
#: apps/rules/views.py:241 apps/transactions/views/categories.py:142
#: apps/transactions/views/entities.py:184 apps/transactions/views/tags.py:184
msgid "Configuration saved successfully"
@@ -357,7 +363,7 @@ msgid "Public"
msgstr ""
#: apps/common/templatetags/natural.py:20
#: templates/monthly_overview/fragments/monthly_summary.html:9
#: templates/monthly_overview/fragments/monthly_summary.html:10
msgid "today"
msgstr ""
@@ -455,10 +461,10 @@ msgstr ""
#: apps/common/widgets/tom_select.py:15
#: templates/mini_tools/unit_price_calculator.html:180
#: templates/monthly_overview/pages/overview.html:171
#: templates/monthly_overview/pages/overview.html:183
#: templates/transactions/pages/transactions.html:124
#: templates/transactions/pages/transactions.html:136
#: templates/monthly_overview/pages/overview.html:275
#: templates/monthly_overview/pages/overview.html:287
#: templates/transactions/pages/transactions.html:225
#: templates/transactions/pages/transactions.html:237
msgid "Clear"
msgstr ""
@@ -497,11 +503,11 @@ msgid "Decimal Places"
msgstr ""
#: apps/currencies/models.py:45 apps/export_app/forms.py:25
#: apps/export_app/forms.py:130 apps/transactions/filters.py:59
#: apps/export_app/forms.py:130 apps/transactions/filters.py:64
#: templates/currencies/fragments/list.html:9
#: templates/currencies/pages/index.html:4 templates/includes/sidebar.html:176
#: templates/includes/sidebar.html:178
#: templates/monthly_overview/pages/overview.html:62
#: templates/monthly_overview/pages/overview.html:63
#: templates/transactions/pages/transactions.html:12
msgid "Currencies"
msgstr ""
@@ -562,9 +568,9 @@ msgstr ""
msgid "Service Type"
msgstr ""
#: apps/currencies/models.py:118 apps/transactions/models.py:218
#: apps/transactions/models.py:242 apps/transactions/models.py:266
#: templates/categories/fragments/list.html:16
#: apps/currencies/models.py:118 apps/transactions/filters.py:27
#: apps/transactions/models.py:218 apps/transactions/models.py:242
#: apps/transactions/models.py:266 templates/categories/fragments/list.html:16
#: templates/entities/fragments/list.html:16
#: templates/installment_plans/fragments/list.html:16
#: templates/recurring_transactions/fragments/list.html:16
@@ -592,57 +598,57 @@ msgstr ""
msgid "Last Successful Fetch"
msgstr ""
#: apps/currencies/models.py:141
#: apps/currencies/models.py:143
msgid "Target Currencies"
msgstr ""
#: apps/currencies/models.py:143
#: apps/currencies/models.py:145
msgid ""
"Select currencies to fetch exchange rates for. Rates will be fetched for "
"each currency against their set exchange currency."
msgstr ""
#: apps/currencies/models.py:151
#: apps/currencies/models.py:153
msgid "Target Accounts"
msgstr ""
#: apps/currencies/models.py:153
#: apps/currencies/models.py:155
msgid ""
"Select accounts to fetch exchange rates for. Rates will be fetched for each "
"account's currency against their set exchange currency."
msgstr ""
#: apps/currencies/models.py:160
#: apps/currencies/models.py:162
msgid "Single exchange rate"
msgstr ""
#: apps/currencies/models.py:163
#: apps/currencies/models.py:165
msgid "Create one exchange rate and keep updating it. Avoids database clutter."
msgstr ""
#: apps/currencies/models.py:168
#: apps/currencies/models.py:170
msgid "Exchange Rate Service"
msgstr ""
#: apps/currencies/models.py:169
#: apps/currencies/models.py:171
msgid "Exchange Rate Services"
msgstr ""
#: apps/currencies/models.py:221
#: apps/currencies/models.py:223
msgid "'Every X hours' interval type requires a positive integer."
msgstr ""
#: apps/currencies/models.py:230
#: apps/currencies/models.py:232
msgid "'Every X hours' interval must be between 1 and 24."
msgstr ""
#: apps/currencies/models.py:244
#: apps/currencies/models.py:246
msgid ""
"Invalid hour format. Use comma-separated hours (0-23) and/or ranges (e.g., "
"'1-5,8,10-12')."
msgstr ""
#: apps/currencies/models.py:255
#: apps/currencies/models.py:257
msgid ""
"Invalid format. Please check the requirements for your selected interval "
"type."
@@ -741,8 +747,8 @@ msgstr ""
#: apps/dca/models.py:26 apps/dca/models.py:181 apps/rules/forms.py:180
#: apps/rules/forms.py:196 apps/rules/models.py:43 apps/rules/models.py:295
#: apps/transactions/forms.py:413 apps/transactions/forms.py:560
#: apps/transactions/models.py:318 apps/transactions/models.py:583
#: apps/transactions/models.py:784 apps/transactions/models.py:1018
#: apps/transactions/models.py:318 apps/transactions/models.py:587
#: apps/transactions/models.py:788 apps/transactions/models.py:1022
msgid "Notes"
msgstr ""
@@ -774,27 +780,27 @@ msgstr ""
msgid "DCA Entries"
msgstr ""
#: apps/dca/views.py:39
#: apps/dca/views.py:38
msgid "DCA Strategy added successfully"
msgstr ""
#: apps/dca/views.py:76
#: apps/dca/views.py:75
msgid "DCA Strategy updated successfully"
msgstr ""
#: apps/dca/views.py:108
#: apps/dca/views.py:107
msgid "DCA strategy deleted successfully"
msgstr ""
#: apps/dca/views.py:238
#: apps/dca/views.py:237
msgid "Entry added successfully"
msgstr ""
#: apps/dca/views.py:265
#: apps/dca/views.py:264
msgid "Entry updated successfully"
msgstr ""
#: apps/dca/views.py:291
#: apps/dca/views.py:290
msgid "Entry deleted successfully"
msgstr ""
@@ -814,34 +820,42 @@ msgid "Transactions"
msgstr ""
#: apps/export_app/forms.py:37 apps/export_app/forms.py:131
#: apps/transactions/filters.py:63 templates/categories/fragments/list.html:9
#: apps/transactions/filters.py:68 templates/categories/fragments/list.html:9
#: templates/categories/pages/index.html:4 templates/includes/sidebar.html:144
#: templates/insights/fragments/month_by_month.html:18
#: templates/insights/fragments/month_by_month.html:21
#: templates/insights/fragments/year_by_year.html:13
#: templates/insights/fragments/year_by_year.html:16
msgid "Categories"
msgstr ""
#: apps/export_app/forms.py:49 apps/export_app/forms.py:133
#: apps/rules/forms.py:185 apps/rules/forms.py:195 apps/rules/models.py:46
#: apps/rules/models.py:307 apps/transactions/filters.py:73
#: apps/rules/models.py:307 apps/transactions/filters.py:78
#: apps/transactions/forms.py:59 apps/transactions/forms.py:267
#: apps/transactions/forms.py:435 apps/transactions/forms.py:715
#: apps/transactions/forms.py:956 apps/transactions/models.py:277
#: apps/transactions/models.py:333 apps/transactions/models.py:579
#: apps/transactions/models.py:781 apps/transactions/models.py:1033
#: apps/transactions/models.py:333 apps/transactions/models.py:583
#: apps/transactions/models.py:785 apps/transactions/models.py:1037
#: templates/entities/fragments/list.html:9
#: templates/entities/pages/index.html:4 templates/includes/sidebar.html:156
#: templates/insights/fragments/category_overview/index.html:54
#: templates/insights/fragments/month_by_month.html:40
#: templates/insights/fragments/month_by_month.html:43
#: templates/insights/fragments/year_by_year.html:35
#: templates/insights/fragments/year_by_year.html:38
msgid "Entities"
msgstr ""
#: apps/export_app/forms.py:55 apps/export_app/forms.py:137
#: apps/transactions/models.py:821 templates/includes/sidebar.html:110
#: apps/transactions/models.py:825 templates/includes/sidebar.html:110
#: templates/recurring_transactions/fragments/list.html:9
#: templates/recurring_transactions/pages/index.html:4
msgid "Recurring Transactions"
msgstr ""
#: apps/export_app/forms.py:61 apps/export_app/forms.py:135
#: apps/transactions/models.py:597 templates/includes/sidebar.html:104
#: apps/transactions/models.py:601 templates/includes/sidebar.html:104
#: templates/installment_plans/fragments/list.html:9
#: templates/installment_plans/pages/index.html:4
msgid "Installment Plans"
@@ -993,10 +1007,12 @@ msgid "Run deleted successfully"
msgstr ""
#: apps/insights/forms.py:118 apps/insights/utils/sankey.py:36
#: apps/insights/utils/sankey.py:167 apps/transactions/filters.py:186
#: apps/insights/utils/sankey.py:167 apps/transactions/filters.py:203
#: templates/insights/fragments/category_overview/index.html:96
#: templates/insights/fragments/category_overview/index.html:407
#: templates/insights/fragments/category_overview/index.html:436
#: templates/insights/fragments/month_by_month.html:119
#: templates/insights/fragments/year_by_year.html:73
msgid "Uncategorized"
msgstr ""
@@ -1089,15 +1105,15 @@ msgstr ""
#: apps/rules/forms.py:174 apps/rules/forms.py:188 apps/rules/models.py:36
#: apps/rules/models.py:271 apps/transactions/forms.py:377
#: apps/transactions/models.py:301 apps/transactions/models.py:539
#: apps/transactions/models.py:762 apps/transactions/models.py:1003
#: apps/transactions/models.py:301 apps/transactions/models.py:543
#: apps/transactions/models.py:766 apps/transactions/models.py:1007
msgid "Type"
msgstr ""
#: apps/rules/forms.py:175 apps/rules/forms.py:189 apps/rules/models.py:37
#: apps/rules/models.py:275 apps/transactions/filters.py:22
#: apps/transactions/forms.py:381 apps/transactions/models.py:303
#: apps/transactions/models.py:1005 templates/cotton/transaction/item.html:20
#: apps/transactions/models.py:1009 templates/cotton/transaction/item.html:20
#: templates/cotton/transaction/item.html:31
#: templates/transactions/widgets/paid_toggle_button.html:10
#: templates/transactions/widgets/unselectable_paid_toggle_button.html:13
@@ -1108,14 +1124,14 @@ msgstr ""
#: apps/rules/models.py:283 apps/transactions/forms.py:71
#: apps/transactions/forms.py:397 apps/transactions/forms.py:547
#: apps/transactions/forms.py:721 apps/transactions/models.py:305
#: apps/transactions/models.py:557 apps/transactions/models.py:786
#: apps/transactions/models.py:561 apps/transactions/models.py:790
msgid "Reference Date"
msgstr ""
#: apps/rules/forms.py:178 apps/rules/forms.py:192 apps/rules/models.py:41
#: apps/rules/models.py:287 apps/transactions/forms.py:404
#: apps/transactions/models.py:311 apps/transactions/models.py:767
#: apps/transactions/models.py:1011
#: apps/transactions/models.py:311 apps/transactions/models.py:771
#: apps/transactions/models.py:1015
#: templates/insights/fragments/sankey.html:102
#: templates/installment_plans/fragments/table.html:18
#: templates/quick_transactions/fragments/list.html:15
@@ -1126,27 +1142,27 @@ msgstr ""
#: apps/rules/forms.py:179 apps/rules/forms.py:193 apps/rules/models.py:14
#: apps/rules/models.py:42 apps/rules/models.py:291
#: apps/transactions/forms.py:408 apps/transactions/forms.py:551
#: apps/transactions/models.py:316 apps/transactions/models.py:541
#: apps/transactions/models.py:770 apps/transactions/models.py:1016
#: apps/transactions/models.py:316 apps/transactions/models.py:545
#: apps/transactions/models.py:774 apps/transactions/models.py:1020
msgid "Description"
msgstr ""
#: apps/rules/forms.py:182 apps/rules/forms.py:198 apps/rules/models.py:47
#: apps/rules/models.py:299 apps/transactions/models.py:355
#: apps/transactions/models.py:1038
#: apps/transactions/models.py:1042
msgid "Internal Note"
msgstr ""
#: apps/rules/forms.py:183 apps/rules/forms.py:199 apps/rules/models.py:48
#: apps/rules/models.py:303 apps/transactions/models.py:357
#: apps/transactions/models.py:1040
#: apps/transactions/models.py:1044
msgid "Internal ID"
msgstr ""
#: apps/rules/forms.py:186 apps/rules/forms.py:200 apps/rules/models.py:40
#: apps/rules/models.py:319 apps/transactions/forms.py:564
#: apps/transactions/models.py:215 apps/transactions/models.py:306
#: apps/transactions/models.py:1006
#: apps/transactions/models.py:1010
msgid "Mute"
msgstr ""
@@ -1302,53 +1318,65 @@ msgstr ""
msgid "Projected"
msgstr ""
#: apps/transactions/filters.py:40
#: apps/transactions/filters.py:28 templates/categories/fragments/table.html:18
msgid "Muted"
msgstr ""
#: apps/transactions/filters.py:45
msgid "Content"
msgstr ""
#: apps/transactions/filters.py:46
#: apps/transactions/filters.py:51
msgid "Transaction Type"
msgstr ""
#: apps/transactions/filters.py:84
msgid "Date from"
msgstr ""
#: apps/transactions/filters.py:89 apps/transactions/filters.py:99
msgid "Until"
#: apps/transactions/filters.py:89
msgid "Mute Status"
msgstr ""
#: apps/transactions/filters.py:94
msgid "Reference date from"
msgid "Date from"
msgstr ""
#: apps/transactions/filters.py:99 apps/transactions/filters.py:109
msgid "Until"
msgstr ""
#: apps/transactions/filters.py:104
msgid "Reference date from"
msgstr ""
#: apps/transactions/filters.py:114
msgid "Amount min"
msgstr ""
#: apps/transactions/filters.py:109
#: apps/transactions/filters.py:119
msgid "Amount max"
msgstr ""
#: apps/transactions/filters.py:185
#: apps/transactions/filters.py:202
msgid "Categorized"
msgstr ""
#: apps/transactions/filters.py:192
#: apps/transactions/filters.py:209
msgid "Tagged"
msgstr ""
#: apps/transactions/filters.py:192
#: apps/transactions/filters.py:209
#: templates/insights/fragments/category_overview/index.html:189
#: templates/insights/fragments/month_by_month.html:121
#: templates/insights/fragments/year_by_year.html:75
msgid "Untagged"
msgstr ""
#: apps/transactions/filters.py:198
#: apps/transactions/filters.py:215
msgid "Any entity"
msgstr ""
#: apps/transactions/filters.py:199
#: apps/transactions/filters.py:216
#: templates/insights/fragments/category_overview/index.html:282
#: templates/insights/fragments/month_by_month.html:123
#: templates/insights/fragments/year_by_year.html:77
msgid "No entity"
msgstr ""
@@ -1436,10 +1464,12 @@ msgid ""
msgstr ""
#: apps/transactions/models.py:276
#: templates/insights/fragments/month_by_month.html:88
#: templates/insights/fragments/year_by_year.html:56
msgid "Entity"
msgstr ""
#: apps/transactions/models.py:288 apps/transactions/models.py:983
#: apps/transactions/models.py:288 apps/transactions/models.py:987
#: templates/calendar_view/fragments/list.html:42
#: templates/calendar_view/fragments/list.html:44
#: templates/calendar_view/fragments/list.html:52
@@ -1447,11 +1477,11 @@ msgstr ""
#: templates/cotton/ui/quick_transactions_buttons.html:10
#: templates/cotton/ui/transactions_fab.html:10
#: templates/insights/fragments/category_overview/index.html:87
#: templates/monthly_overview/fragments/monthly_summary.html:39
#: templates/monthly_overview/fragments/monthly_summary.html:41
msgid "Income"
msgstr ""
#: apps/transactions/models.py:289 apps/transactions/models.py:984
#: apps/transactions/models.py:289 apps/transactions/models.py:988
#: templates/calendar_view/fragments/list.html:46
#: templates/calendar_view/fragments/list.html:48
#: templates/calendar_view/fragments/list.html:56
@@ -1462,11 +1492,11 @@ msgstr ""
msgid "Expense"
msgstr ""
#: apps/transactions/models.py:344 apps/transactions/models.py:596
#: apps/transactions/models.py:344 apps/transactions/models.py:600
msgid "Installment Plan"
msgstr ""
#: apps/transactions/models.py:353 apps/transactions/models.py:820
#: apps/transactions/models.py:353 apps/transactions/models.py:824
msgid "Recurring Transaction"
msgstr ""
@@ -1478,113 +1508,113 @@ msgstr ""
msgid "Deleted At"
msgstr ""
#: apps/transactions/models.py:476 templates/tags/fragments/table.html:69
#: apps/transactions/models.py:480 templates/tags/fragments/table.html:69
msgid "No tags"
msgstr ""
#: apps/transactions/models.py:478
#: apps/transactions/models.py:482
msgid "No category"
msgstr ""
#: apps/transactions/models.py:480
#: apps/transactions/models.py:484
msgid "No description"
msgstr ""
#: apps/transactions/models.py:528 templates/includes/sidebar.html:57
#: apps/transactions/models.py:532 templates/includes/sidebar.html:57
msgid "Yearly"
msgstr ""
#: apps/transactions/models.py:529 apps/users/models.py:464
#: apps/transactions/models.py:533 apps/users/models.py:464
#: templates/includes/sidebar.html:51
msgid "Monthly"
msgstr ""
#: apps/transactions/models.py:530
#: apps/transactions/models.py:534
msgid "Weekly"
msgstr ""
#: apps/transactions/models.py:531
#: apps/transactions/models.py:535
msgid "Daily"
msgstr ""
#: apps/transactions/models.py:544
#: apps/transactions/models.py:548
msgid "Number of Installments"
msgstr ""
#: apps/transactions/models.py:549
#: apps/transactions/models.py:553
msgid "Installment Start"
msgstr ""
#: apps/transactions/models.py:550
#: apps/transactions/models.py:554
msgid "The installment number to start counting from"
msgstr ""
#: apps/transactions/models.py:555 apps/transactions/models.py:790
#: apps/transactions/models.py:559 apps/transactions/models.py:794
msgid "Start Date"
msgstr ""
#: apps/transactions/models.py:559 apps/transactions/models.py:791
#: apps/transactions/models.py:563 apps/transactions/models.py:795
msgid "End Date"
msgstr ""
#: apps/transactions/models.py:564
#: apps/transactions/models.py:568
msgid "Recurrence"
msgstr ""
#: apps/transactions/models.py:567
#: apps/transactions/models.py:571
msgid "Installment Amount"
msgstr ""
#: apps/transactions/models.py:586 apps/transactions/models.py:810
#: apps/transactions/models.py:590 apps/transactions/models.py:814
msgid "Add description to transactions"
msgstr ""
#: apps/transactions/models.py:589 apps/transactions/models.py:813
#: apps/transactions/models.py:593 apps/transactions/models.py:817
msgid "Add notes to transactions"
msgstr ""
#: apps/transactions/models.py:749
#: apps/transactions/models.py:753
msgid "day(s)"
msgstr ""
#: apps/transactions/models.py:750
#: apps/transactions/models.py:754
msgid "week(s)"
msgstr ""
#: apps/transactions/models.py:751
#: apps/transactions/models.py:755
msgid "month(s)"
msgstr ""
#: apps/transactions/models.py:752
#: apps/transactions/models.py:756
msgid "year(s)"
msgstr ""
#: apps/transactions/models.py:754
#: apps/transactions/models.py:758
#: templates/recurring_transactions/fragments/list.html:18
msgid "Paused"
msgstr ""
#: apps/transactions/models.py:793
#: apps/transactions/models.py:797
msgid "Recurrence Type"
msgstr ""
#: apps/transactions/models.py:796
#: apps/transactions/models.py:800
msgid "Recurrence Interval"
msgstr ""
#: apps/transactions/models.py:799
#: apps/transactions/models.py:803
msgid "Keep at most"
msgstr ""
#: apps/transactions/models.py:803
#: apps/transactions/models.py:807
msgid "Last Generated Date"
msgstr ""
#: apps/transactions/models.py:806
#: apps/transactions/models.py:810
msgid "Last Generated Reference Date"
msgstr ""
#: apps/transactions/models.py:1050
#: apps/transactions/models.py:1054
#: apps/transactions/views/quick_transactions.py:178
#: apps/transactions/views/quick_transactions.py:187
#: apps/transactions/views/quick_transactions.py:189
@@ -1593,7 +1623,7 @@ msgstr ""
msgid "Quick Transaction"
msgstr ""
#: apps/transactions/models.py:1051 templates/includes/sidebar.html:98
#: apps/transactions/models.py:1055 templates/includes/sidebar.html:98
#: templates/quick_transactions/pages/index.html:5
#: templates/quick_transactions/pages/index.html:15
msgid "Quick Transactions"
@@ -1699,7 +1729,7 @@ msgstr ""
#: apps/transactions/views/quick_transactions.py:156
#: apps/transactions/views/transactions.py:53
#: apps/transactions/views/transactions.py:228
#: apps/transactions/views/transactions.py:238
msgid "Transaction added successfully"
msgstr ""
@@ -1739,30 +1769,30 @@ msgstr ""
msgid "Tag deleted successfully"
msgstr ""
#: apps/transactions/views/transactions.py:252
#: apps/transactions/views/transactions.py:262
msgid "Transaction updated successfully"
msgstr ""
#: apps/transactions/views/transactions.py:303
#: apps/transactions/views/transactions.py:313
#, python-format
msgid "%(count)s transaction updated successfully"
msgid_plural "%(count)s transactions updated successfully"
msgstr[0] ""
msgstr[1] ""
#: apps/transactions/views/transactions.py:339
#: apps/transactions/views/transactions.py:349
msgid "Transaction duplicated successfully"
msgstr ""
#: apps/transactions/views/transactions.py:381
#: apps/transactions/views/transactions.py:391
msgid "Transaction deleted successfully"
msgstr ""
#: apps/transactions/views/transactions.py:399
#: apps/transactions/views/transactions.py:409
msgid "Transaction restored successfully"
msgstr ""
#: apps/transactions/views/transactions.py:425
#: apps/transactions/views/transactions.py:435
msgid "Transfer added successfully"
msgstr ""
@@ -1804,10 +1834,10 @@ msgid "This account is deactivated"
msgstr ""
#: apps/users/forms.py:62 apps/users/forms.py:75 apps/users/forms.py:97
#: templates/monthly_overview/pages/overview.html:95
#: templates/monthly_overview/pages/overview.html:141
#: templates/monthly_overview/pages/overview.html:98
#: templates/monthly_overview/pages/overview.html:245
#: templates/transactions/pages/transactions.html:47
#: templates/transactions/pages/transactions.html:94
#: templates/transactions/pages/transactions.html:195
msgid "Default"
msgstr ""
@@ -2227,10 +2257,6 @@ msgstr ""
msgid "Edit category"
msgstr ""
#: templates/categories/fragments/table.html:18
msgid "Muted"
msgstr ""
#: templates/categories/fragments/table.html:73
#: templates/insights/fragments/category_overview/index.html:552
msgid "No categories"
@@ -2249,9 +2275,9 @@ msgstr ""
#: templates/cotton/config/search.html:6
#: templates/import_app/fragments/profiles/list_presets.html:13
#: templates/monthly_overview/pages/overview.html:115
#: templates/monthly_overview/pages/overview.html:219
#: templates/rules/fragments/transaction_rule/dry_run/visual.html:57
#: templates/transactions/pages/transactions.html:67
#: templates/transactions/pages/transactions.html:168
msgid "Search"
msgstr ""
@@ -2479,8 +2505,8 @@ msgid "No entries for this DCA"
msgstr ""
#: templates/dca/fragments/strategy/details.html:120
#: templates/monthly_overview/fragments/list.html:33
#: templates/transactions/fragments/list_all.html:33
#: templates/monthly_overview/fragments/list.html:59
#: templates/transactions/fragments/list_all.html:59
msgid "Try adding one"
msgstr ""
@@ -2605,7 +2631,7 @@ msgstr ""
#: templates/exchange_rates/fragments/table.html:56
#: templates/exchange_rates_services/fragments/table.html:57
#: templates/transactions/fragments/list_all.html:43
#: templates/transactions/fragments/list_all.html:70
msgid "Page navigation"
msgstr ""
@@ -2625,15 +2651,23 @@ msgstr ""
msgid "Last fetch"
msgstr ""
#: templates/exchange_rates_services/fragments/list.html:61
#: templates/exchange_rates_services/fragments/list.html:62
#, python-format
msgid "%(counter)s consecutive failure"
msgid_plural "%(counter)s consecutive failures"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
#: templates/exchange_rates_services/fragments/list.html:69
msgid "currencies"
msgstr ""
#: templates/exchange_rates_services/fragments/list.html:61
#: templates/exchange_rates_services/fragments/list.html:69
msgid "accounts"
msgstr ""
#: templates/exchange_rates_services/fragments/list.html:69
#: templates/exchange_rates_services/fragments/list.html:77
msgid "No services configured"
msgstr ""
@@ -2883,7 +2917,11 @@ msgid "Final total"
msgstr ""
#: templates/insights/fragments/category_overview/index.html:89
#: templates/monthly_overview/fragments/monthly_summary.html:165
#: templates/insights/fragments/month_by_month.html:91
#: templates/insights/fragments/month_by_month.html:186
#: templates/insights/fragments/year_by_year.html:59
#: templates/insights/fragments/year_by_year.html:140
#: templates/monthly_overview/fragments/monthly_summary.html:167
msgid "Total"
msgstr ""
@@ -2927,6 +2965,65 @@ msgstr ""
msgid "No recent transactions"
msgstr ""
#: templates/insights/fragments/month_by_month.html:86
#: templates/insights/fragments/year_by_year.html:54
#, fuzzy
#| msgid "Tags"
msgid "Tag"
msgstr "Tagi"
#: templates/insights/fragments/month_by_month.html:94
msgid "Jan"
msgstr ""
#: templates/insights/fragments/month_by_month.html:95
msgid "Feb"
msgstr ""
#: templates/insights/fragments/month_by_month.html:96
msgid "Mar"
msgstr ""
#: templates/insights/fragments/month_by_month.html:97
msgid "Apr"
msgstr ""
#: templates/insights/fragments/month_by_month.html:98
msgid "May"
msgstr ""
#: templates/insights/fragments/month_by_month.html:99
msgid "Jun"
msgstr ""
#: templates/insights/fragments/month_by_month.html:100
msgid "Jul"
msgstr ""
#: templates/insights/fragments/month_by_month.html:101
msgid "Aug"
msgstr ""
#: templates/insights/fragments/month_by_month.html:102
msgid "Sep"
msgstr ""
#: templates/insights/fragments/month_by_month.html:103
msgid "Oct"
msgstr ""
#: templates/insights/fragments/month_by_month.html:104
msgid "Nov"
msgstr ""
#: templates/insights/fragments/month_by_month.html:105
msgid "Dec"
msgstr ""
#: templates/insights/fragments/month_by_month.html:248
msgid "No transactions for this year"
msgstr ""
#: templates/insights/fragments/sankey.html:100
msgid "From"
msgstr ""
@@ -2935,6 +3032,10 @@ msgstr ""
msgid "Percentage"
msgstr ""
#: templates/insights/fragments/year_by_year.html:202
msgid "No transactions"
msgstr ""
#: templates/insights/pages/index.html:37
msgid "Month"
msgstr ""
@@ -2984,6 +3085,14 @@ msgstr ""
msgid "Emergency Fund"
msgstr ""
#: templates/insights/pages/index.html:127
msgid "Year by Year"
msgstr ""
#: templates/insights/pages/index.html:132
msgid "Month by Month"
msgstr ""
#: templates/installment_plans/fragments/add.html:5
msgid "Add installment plan"
msgstr ""
@@ -3055,35 +3164,40 @@ msgstr ""
msgid "Item"
msgstr ""
#: templates/monthly_overview/fragments/list.html:32
#: templates/monthly_overview/fragments/list.html:15
#: templates/transactions/fragments/list_all.html:15
msgid "late"
msgstr ""
#: templates/monthly_overview/fragments/list.html:58
msgid "No transactions this month"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:6
#: templates/monthly_overview/fragments/monthly_summary.html:7
msgid "Daily Spending Allowance"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:6
#: templates/monthly_overview/fragments/monthly_summary.html:7
msgid "This is the final total divided by the remaining days in the month"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:42
#: templates/monthly_overview/fragments/monthly_summary.html:105
#: templates/monthly_overview/fragments/monthly_summary.html:168
#: templates/monthly_overview/fragments/monthly_summary.html:44
#: templates/monthly_overview/fragments/monthly_summary.html:107
#: templates/monthly_overview/fragments/monthly_summary.html:170
msgid "current"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:71
#: templates/monthly_overview/fragments/monthly_summary.html:134
#: templates/monthly_overview/fragments/monthly_summary.html:197
#: templates/monthly_overview/fragments/monthly_summary.html:73
#: templates/monthly_overview/fragments/monthly_summary.html:136
#: templates/monthly_overview/fragments/monthly_summary.html:199
msgid "projected"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:102
#: templates/monthly_overview/fragments/monthly_summary.html:104
msgid "Expenses"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:255
#: templates/monthly_overview/fragments/monthly_summary.html:257
msgid "Distribution"
msgstr ""
@@ -3091,27 +3205,27 @@ msgstr ""
msgid "Summary"
msgstr ""
#: templates/monthly_overview/pages/overview.html:96
#: templates/monthly_overview/pages/overview.html:150
#: templates/monthly_overview/pages/overview.html:99
#: templates/monthly_overview/pages/overview.html:254
#: templates/transactions/pages/transactions.html:48
#: templates/transactions/pages/transactions.html:103
#: templates/transactions/pages/transactions.html:204
msgid "Oldest first"
msgstr ""
#: templates/monthly_overview/pages/overview.html:97
#: templates/monthly_overview/pages/overview.html:159
#: templates/monthly_overview/pages/overview.html:100
#: templates/monthly_overview/pages/overview.html:263
#: templates/transactions/pages/transactions.html:49
#: templates/transactions/pages/transactions.html:112
#: templates/transactions/pages/transactions.html:213
msgid "Newest first"
msgstr ""
#: templates/monthly_overview/pages/overview.html:106
#: templates/monthly_overview/pages/overview.html:109
#: templates/transactions/pages/transactions.html:58
msgid "Filter transactions"
msgstr ""
#: templates/monthly_overview/pages/overview.html:131
#: templates/transactions/pages/transactions.html:84
#: templates/monthly_overview/pages/overview.html:235
#: templates/transactions/pages/transactions.html:185
msgid "Order by"
msgstr ""
@@ -3352,7 +3466,7 @@ msgstr ""
msgid "transactions"
msgstr ""
#: templates/transactions/fragments/list_all.html:32
#: templates/transactions/fragments/list_all.html:58
msgid "No transactions found"
msgstr ""

View File

@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-12-20 02:59+0000\n"
"PO-Revision-Date: 2025-12-14 15:07+0000\n"
"POT-Creation-Date: 2026-01-10 20:50+0000\n"
"PO-Revision-Date: 2026-01-11 16:55+0000\n"
"Last-Translator: Herculino Trotta <netotrotta@gmail.com>\n"
"Language-Team: Portuguese (Brazil) <https://translations.herculino.com/"
"projects/wygiwyh/app/pt_BR/>\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.14.3\n"
"X-Generator: Weblate 5.15.1\n"
#: apps/accounts/forms.py:24
msgid "Group name"
@@ -67,24 +67,30 @@ msgstr "Novo saldo"
#: apps/transactions/forms.py:419 apps/transactions/forms.py:516
#: apps/transactions/forms.py:523 apps/transactions/forms.py:707
#: apps/transactions/forms.py:948 apps/transactions/models.py:322
#: apps/transactions/models.py:574 apps/transactions/models.py:774
#: apps/transactions/models.py:1022
#: apps/transactions/models.py:578 apps/transactions/models.py:778
#: apps/transactions/models.py:1026
#: templates/insights/fragments/category_overview/index.html:86
#: templates/insights/fragments/category_overview/index.html:542
#: templates/insights/fragments/month_by_month.html:84
#: templates/insights/fragments/year_by_year.html:52
msgid "Category"
msgstr "Categoria"
#: apps/accounts/forms.py:132 apps/dca/forms.py:95 apps/dca/forms.py:103
#: apps/export_app/forms.py:43 apps/export_app/forms.py:132
#: apps/rules/forms.py:184 apps/rules/forms.py:194 apps/rules/models.py:45
#: apps/rules/models.py:315 apps/transactions/filters.py:68
#: apps/rules/models.py:315 apps/transactions/filters.py:73
#: apps/transactions/forms.py:51 apps/transactions/forms.py:259
#: apps/transactions/forms.py:427 apps/transactions/forms.py:532
#: apps/transactions/forms.py:540 apps/transactions/forms.py:700
#: apps/transactions/forms.py:941 apps/transactions/models.py:328
#: apps/transactions/models.py:576 apps/transactions/models.py:778
#: apps/transactions/models.py:1028 templates/includes/sidebar.html:150
#: apps/transactions/models.py:580 apps/transactions/models.py:782
#: apps/transactions/models.py:1032 templates/includes/sidebar.html:150
#: templates/insights/fragments/category_overview/index.html:40
#: templates/insights/fragments/month_by_month.html:29
#: templates/insights/fragments/month_by_month.html:32
#: templates/insights/fragments/year_by_year.html:24
#: templates/insights/fragments/year_by_year.html:27
#: templates/tags/fragments/list.html:9 templates/tags/pages/index.html:4
msgid "Tags"
msgstr "Tags"
@@ -92,7 +98,7 @@ msgstr "Tags"
#: apps/accounts/models.py:12 apps/accounts/models.py:29 apps/dca/models.py:13
#: apps/import_app/models.py:14 apps/rules/models.py:13
#: apps/transactions/models.py:214 apps/transactions/models.py:239
#: apps/transactions/models.py:263 apps/transactions/models.py:990
#: apps/transactions/models.py:263 apps/transactions/models.py:994
#: templates/account_groups/fragments/list.html:22
#: templates/accounts/fragments/list.html:22
#: templates/categories/fragments/table.html:17
@@ -164,8 +170,8 @@ msgstr ""
#: apps/transactions/forms.py:63 apps/transactions/forms.py:271
#: apps/transactions/forms.py:386 apps/transactions/forms.py:692
#: apps/transactions/forms.py:933 apps/transactions/models.py:294
#: apps/transactions/models.py:534 apps/transactions/models.py:756
#: apps/transactions/models.py:996
#: apps/transactions/models.py:538 apps/transactions/models.py:760
#: apps/transactions/models.py:1000
#: templates/installment_plans/fragments/table.html:17
#: templates/quick_transactions/fragments/list.html:14
#: templates/recurring_transactions/fragments/table.html:19
@@ -174,11 +180,11 @@ msgid "Account"
msgstr "Conta"
#: apps/accounts/models.py:76 apps/export_app/forms.py:19
#: apps/export_app/forms.py:129 apps/transactions/filters.py:52
#: apps/export_app/forms.py:129 apps/transactions/filters.py:57
#: templates/accounts/fragments/list.html:9
#: templates/accounts/pages/index.html:4 templates/includes/sidebar.html:162
#: templates/includes/sidebar.html:164
#: templates/monthly_overview/pages/overview.html:75
#: templates/monthly_overview/pages/overview.html:77
#: templates/transactions/pages/transactions.html:26
msgid "Accounts"
msgstr "Contas"
@@ -193,8 +199,8 @@ msgstr "Grupo de Conta adicionado com sucesso"
#: apps/accounts/views/account_groups.py:69
#: apps/accounts/views/account_groups.py:152 apps/accounts/views/accounts.py:68
#: apps/accounts/views/accounts.py:106 apps/dca/views.py:63
#: apps/dca/views.py:146 apps/rules/views.py:118 apps/rules/views.py:228
#: apps/accounts/views/accounts.py:106 apps/dca/views.py:62
#: apps/dca/views.py:145 apps/rules/views.py:118 apps/rules/views.py:228
#: apps/transactions/views/categories.py:91
#: apps/transactions/views/categories.py:129
#: apps/transactions/views/entities.py:91
@@ -208,7 +214,7 @@ msgid "Account Group updated successfully"
msgstr "Grupo de Conta atualizado com sucesso"
#: apps/accounts/views/account_groups.py:111
#: apps/accounts/views/accounts.py:145 apps/dca/views.py:105
#: apps/accounts/views/accounts.py:145 apps/dca/views.py:104
#: apps/rules/views.py:185 apps/transactions/views/categories.py:168
#: apps/transactions/views/entities.py:130 apps/transactions/views/tags.py:130
msgid "Item no longer shared with you"
@@ -219,14 +225,14 @@ msgid "Account Group deleted successfully"
msgstr "Grupo de Conta apagado com sucesso"
#: apps/accounts/views/account_groups.py:135
#: apps/accounts/views/accounts.py:189 apps/dca/views.py:129
#: apps/accounts/views/accounts.py:189 apps/dca/views.py:128
#: apps/rules/views.py:210 apps/transactions/views/categories.py:192
#: apps/transactions/views/entities.py:154 apps/transactions/views/tags.py:154
msgid "Ownership taken successfully"
msgstr "Propriedade assumida com sucesso"
#: apps/accounts/views/account_groups.py:165
#: apps/accounts/views/accounts.py:119 apps/dca/views.py:159
#: apps/accounts/views/accounts.py:119 apps/dca/views.py:158
#: apps/rules/views.py:241 apps/transactions/views/categories.py:142
#: apps/transactions/views/entities.py:184 apps/transactions/views/tags.py:184
msgid "Configuration saved successfully"
@@ -364,7 +370,7 @@ msgid "Public"
msgstr "Público"
#: apps/common/templatetags/natural.py:20
#: templates/monthly_overview/fragments/monthly_summary.html:9
#: templates/monthly_overview/fragments/monthly_summary.html:10
msgid "today"
msgstr "hoje"
@@ -462,10 +468,10 @@ msgstr "Remover"
#: apps/common/widgets/tom_select.py:15
#: templates/mini_tools/unit_price_calculator.html:180
#: templates/monthly_overview/pages/overview.html:171
#: templates/monthly_overview/pages/overview.html:183
#: templates/transactions/pages/transactions.html:124
#: templates/transactions/pages/transactions.html:136
#: templates/monthly_overview/pages/overview.html:275
#: templates/monthly_overview/pages/overview.html:287
#: templates/transactions/pages/transactions.html:225
#: templates/transactions/pages/transactions.html:237
msgid "Clear"
msgstr "Limpar"
@@ -504,11 +510,11 @@ msgid "Decimal Places"
msgstr "Casas Decimais"
#: apps/currencies/models.py:45 apps/export_app/forms.py:25
#: apps/export_app/forms.py:130 apps/transactions/filters.py:59
#: apps/export_app/forms.py:130 apps/transactions/filters.py:64
#: templates/currencies/fragments/list.html:9
#: templates/currencies/pages/index.html:4 templates/includes/sidebar.html:176
#: templates/includes/sidebar.html:178
#: templates/monthly_overview/pages/overview.html:62
#: templates/monthly_overview/pages/overview.html:63
#: templates/transactions/pages/transactions.html:12
msgid "Currencies"
msgstr "Moedas"
@@ -569,9 +575,9 @@ msgstr "Nome do Serviço"
msgid "Service Type"
msgstr "Tipo de Serviço"
#: apps/currencies/models.py:118 apps/transactions/models.py:218
#: apps/transactions/models.py:242 apps/transactions/models.py:266
#: templates/categories/fragments/list.html:16
#: apps/currencies/models.py:118 apps/transactions/filters.py:27
#: apps/transactions/models.py:218 apps/transactions/models.py:242
#: apps/transactions/models.py:266 templates/categories/fragments/list.html:16
#: templates/entities/fragments/list.html:16
#: templates/installment_plans/fragments/list.html:16
#: templates/recurring_transactions/fragments/list.html:16
@@ -599,11 +605,11 @@ msgstr "Intervalo"
msgid "Last Successful Fetch"
msgstr "Última execução bem-sucedida"
#: apps/currencies/models.py:141
#: apps/currencies/models.py:143
msgid "Target Currencies"
msgstr "Moedas-alvo"
#: apps/currencies/models.py:143
#: apps/currencies/models.py:145
msgid ""
"Select currencies to fetch exchange rates for. Rates will be fetched for "
"each currency against their set exchange currency."
@@ -611,11 +617,11 @@ msgstr ""
"Selecione as moedas para as quais deseja obter as taxas de câmbio. As taxas "
"serão obtidas para cada moeda em relação à moeda de câmbio definida."
#: apps/currencies/models.py:151
#: apps/currencies/models.py:153
msgid "Target Accounts"
msgstr "Contas-alvo"
#: apps/currencies/models.py:153
#: apps/currencies/models.py:155
msgid ""
"Select accounts to fetch exchange rates for. Rates will be fetched for each "
"account's currency against their set exchange currency."
@@ -624,34 +630,34 @@ msgstr ""
"serão obtidas para a moeda de cada conta em relação à moeda de câmbio "
"definida."
#: apps/currencies/models.py:160
#: apps/currencies/models.py:162
msgid "Single exchange rate"
msgstr "Taxa de câmbio única"
#: apps/currencies/models.py:163
#: apps/currencies/models.py:165
msgid "Create one exchange rate and keep updating it. Avoids database clutter."
msgstr ""
"Cria uma taxa de câmbio e mantenha-a atualizada. Evita a poluição do banco "
"de dados."
#: apps/currencies/models.py:168
#: apps/currencies/models.py:170
msgid "Exchange Rate Service"
msgstr "Serviço de Taxa de Câmbio"
#: apps/currencies/models.py:169
#: apps/currencies/models.py:171
msgid "Exchange Rate Services"
msgstr "Serviços de Taxa de Câmbio"
#: apps/currencies/models.py:221
#: apps/currencies/models.py:223
msgid "'Every X hours' interval type requires a positive integer."
msgstr ""
"Intervalo do tipo 'A cada X horas' requerer um número inteiro positivo."
#: apps/currencies/models.py:230
#: apps/currencies/models.py:232
msgid "'Every X hours' interval must be between 1 and 24."
msgstr "Intervalo do tipo 'A cada X horas' requerer um número entre 1 e 24."
#: apps/currencies/models.py:244
#: apps/currencies/models.py:246
msgid ""
"Invalid hour format. Use comma-separated hours (0-23) and/or ranges (e.g., "
"'1-5,8,10-12')."
@@ -659,7 +665,7 @@ msgstr ""
"Formato inválido de hora. Use uma lista de horas separada por vírgulas "
"(0-23) e/ou uma faixa (ex.: '1-5,8,10-12')."
#: apps/currencies/models.py:255
#: apps/currencies/models.py:257
msgid ""
"Invalid format. Please check the requirements for your selected interval "
"type."
@@ -760,8 +766,8 @@ msgstr "Moeda de pagamento"
#: apps/dca/models.py:26 apps/dca/models.py:181 apps/rules/forms.py:180
#: apps/rules/forms.py:196 apps/rules/models.py:43 apps/rules/models.py:295
#: apps/transactions/forms.py:413 apps/transactions/forms.py:560
#: apps/transactions/models.py:318 apps/transactions/models.py:583
#: apps/transactions/models.py:784 apps/transactions/models.py:1018
#: apps/transactions/models.py:318 apps/transactions/models.py:587
#: apps/transactions/models.py:788 apps/transactions/models.py:1022
msgid "Notes"
msgstr "Notas"
@@ -793,27 +799,27 @@ msgstr "Entrada CMP"
msgid "DCA Entries"
msgstr "Entradas CMP"
#: apps/dca/views.py:39
#: apps/dca/views.py:38
msgid "DCA Strategy added successfully"
msgstr "Estratégia CMP adicionada com sucesso"
#: apps/dca/views.py:76
#: apps/dca/views.py:75
msgid "DCA Strategy updated successfully"
msgstr "Estratégia CMP atualizada com sucesso"
#: apps/dca/views.py:108
#: apps/dca/views.py:107
msgid "DCA strategy deleted successfully"
msgstr "Estratégia CMP apagada com sucesso"
#: apps/dca/views.py:238
#: apps/dca/views.py:237
msgid "Entry added successfully"
msgstr "Entrada adicionada com sucesso"
#: apps/dca/views.py:265
#: apps/dca/views.py:264
msgid "Entry updated successfully"
msgstr "Entrada atualizada com sucesso"
#: apps/dca/views.py:291
#: apps/dca/views.py:290
msgid "Entry deleted successfully"
msgstr "Entrada apagada com sucesso"
@@ -833,34 +839,42 @@ msgid "Transactions"
msgstr "Transações"
#: apps/export_app/forms.py:37 apps/export_app/forms.py:131
#: apps/transactions/filters.py:63 templates/categories/fragments/list.html:9
#: apps/transactions/filters.py:68 templates/categories/fragments/list.html:9
#: templates/categories/pages/index.html:4 templates/includes/sidebar.html:144
#: templates/insights/fragments/month_by_month.html:18
#: templates/insights/fragments/month_by_month.html:21
#: templates/insights/fragments/year_by_year.html:13
#: templates/insights/fragments/year_by_year.html:16
msgid "Categories"
msgstr "Categorias"
#: apps/export_app/forms.py:49 apps/export_app/forms.py:133
#: apps/rules/forms.py:185 apps/rules/forms.py:195 apps/rules/models.py:46
#: apps/rules/models.py:307 apps/transactions/filters.py:73
#: apps/rules/models.py:307 apps/transactions/filters.py:78
#: apps/transactions/forms.py:59 apps/transactions/forms.py:267
#: apps/transactions/forms.py:435 apps/transactions/forms.py:715
#: apps/transactions/forms.py:956 apps/transactions/models.py:277
#: apps/transactions/models.py:333 apps/transactions/models.py:579
#: apps/transactions/models.py:781 apps/transactions/models.py:1033
#: apps/transactions/models.py:333 apps/transactions/models.py:583
#: apps/transactions/models.py:785 apps/transactions/models.py:1037
#: templates/entities/fragments/list.html:9
#: templates/entities/pages/index.html:4 templates/includes/sidebar.html:156
#: templates/insights/fragments/category_overview/index.html:54
#: templates/insights/fragments/month_by_month.html:40
#: templates/insights/fragments/month_by_month.html:43
#: templates/insights/fragments/year_by_year.html:35
#: templates/insights/fragments/year_by_year.html:38
msgid "Entities"
msgstr "Entidades"
#: apps/export_app/forms.py:55 apps/export_app/forms.py:137
#: apps/transactions/models.py:821 templates/includes/sidebar.html:110
#: apps/transactions/models.py:825 templates/includes/sidebar.html:110
#: templates/recurring_transactions/fragments/list.html:9
#: templates/recurring_transactions/pages/index.html:4
msgid "Recurring Transactions"
msgstr "Transações Recorrentes"
#: apps/export_app/forms.py:61 apps/export_app/forms.py:135
#: apps/transactions/models.py:597 templates/includes/sidebar.html:104
#: apps/transactions/models.py:601 templates/includes/sidebar.html:104
#: templates/installment_plans/fragments/list.html:9
#: templates/installment_plans/pages/index.html:4
msgid "Installment Plans"
@@ -1014,10 +1028,12 @@ msgid "Run deleted successfully"
msgstr "Importação apagada com sucesso"
#: apps/insights/forms.py:118 apps/insights/utils/sankey.py:36
#: apps/insights/utils/sankey.py:167 apps/transactions/filters.py:186
#: apps/insights/utils/sankey.py:167 apps/transactions/filters.py:203
#: templates/insights/fragments/category_overview/index.html:96
#: templates/insights/fragments/category_overview/index.html:407
#: templates/insights/fragments/category_overview/index.html:436
#: templates/insights/fragments/month_by_month.html:119
#: templates/insights/fragments/year_by_year.html:73
msgid "Uncategorized"
msgstr "Sem categoria"
@@ -1110,15 +1126,15 @@ msgstr "Operador"
#: apps/rules/forms.py:174 apps/rules/forms.py:188 apps/rules/models.py:36
#: apps/rules/models.py:271 apps/transactions/forms.py:377
#: apps/transactions/models.py:301 apps/transactions/models.py:539
#: apps/transactions/models.py:762 apps/transactions/models.py:1003
#: apps/transactions/models.py:301 apps/transactions/models.py:543
#: apps/transactions/models.py:766 apps/transactions/models.py:1007
msgid "Type"
msgstr "Tipo"
#: apps/rules/forms.py:175 apps/rules/forms.py:189 apps/rules/models.py:37
#: apps/rules/models.py:275 apps/transactions/filters.py:22
#: apps/transactions/forms.py:381 apps/transactions/models.py:303
#: apps/transactions/models.py:1005 templates/cotton/transaction/item.html:20
#: apps/transactions/models.py:1009 templates/cotton/transaction/item.html:20
#: templates/cotton/transaction/item.html:31
#: templates/transactions/widgets/paid_toggle_button.html:10
#: templates/transactions/widgets/unselectable_paid_toggle_button.html:13
@@ -1129,14 +1145,14 @@ msgstr "Pago"
#: apps/rules/models.py:283 apps/transactions/forms.py:71
#: apps/transactions/forms.py:397 apps/transactions/forms.py:547
#: apps/transactions/forms.py:721 apps/transactions/models.py:305
#: apps/transactions/models.py:557 apps/transactions/models.py:786
#: apps/transactions/models.py:561 apps/transactions/models.py:790
msgid "Reference Date"
msgstr "Data de Referência"
#: apps/rules/forms.py:178 apps/rules/forms.py:192 apps/rules/models.py:41
#: apps/rules/models.py:287 apps/transactions/forms.py:404
#: apps/transactions/models.py:311 apps/transactions/models.py:767
#: apps/transactions/models.py:1011
#: apps/transactions/models.py:311 apps/transactions/models.py:771
#: apps/transactions/models.py:1015
#: templates/insights/fragments/sankey.html:102
#: templates/installment_plans/fragments/table.html:18
#: templates/quick_transactions/fragments/list.html:15
@@ -1147,27 +1163,27 @@ msgstr "Quantia"
#: apps/rules/forms.py:179 apps/rules/forms.py:193 apps/rules/models.py:14
#: apps/rules/models.py:42 apps/rules/models.py:291
#: apps/transactions/forms.py:408 apps/transactions/forms.py:551
#: apps/transactions/models.py:316 apps/transactions/models.py:541
#: apps/transactions/models.py:770 apps/transactions/models.py:1016
#: apps/transactions/models.py:316 apps/transactions/models.py:545
#: apps/transactions/models.py:774 apps/transactions/models.py:1020
msgid "Description"
msgstr "Descrição"
#: apps/rules/forms.py:182 apps/rules/forms.py:198 apps/rules/models.py:47
#: apps/rules/models.py:299 apps/transactions/models.py:355
#: apps/transactions/models.py:1038
#: apps/transactions/models.py:1042
msgid "Internal Note"
msgstr "Nota Interna"
#: apps/rules/forms.py:183 apps/rules/forms.py:199 apps/rules/models.py:48
#: apps/rules/models.py:303 apps/transactions/models.py:357
#: apps/transactions/models.py:1040
#: apps/transactions/models.py:1044
msgid "Internal ID"
msgstr "ID Interna"
#: apps/rules/forms.py:186 apps/rules/forms.py:200 apps/rules/models.py:40
#: apps/rules/models.py:319 apps/transactions/forms.py:564
#: apps/transactions/models.py:215 apps/transactions/models.py:306
#: apps/transactions/models.py:1006
#: apps/transactions/models.py:1010
msgid "Mute"
msgstr "Silenciada"
@@ -1325,53 +1341,65 @@ msgstr "Ação Atualizar ou Criar Transação apagada com sucesso"
msgid "Projected"
msgstr "Previsto"
#: apps/transactions/filters.py:40
#: apps/transactions/filters.py:28 templates/categories/fragments/table.html:18
msgid "Muted"
msgstr "Silenciada"
#: apps/transactions/filters.py:45
msgid "Content"
msgstr "Conteúdo"
#: apps/transactions/filters.py:46
#: apps/transactions/filters.py:51
msgid "Transaction Type"
msgstr "Tipo de Transação"
#: apps/transactions/filters.py:84
#: apps/transactions/filters.py:89
msgid "Mute Status"
msgstr "Status de silenciamento"
#: apps/transactions/filters.py:94
msgid "Date from"
msgstr "Data de"
#: apps/transactions/filters.py:89 apps/transactions/filters.py:99
#: apps/transactions/filters.py:99 apps/transactions/filters.py:109
msgid "Until"
msgstr "Até"
#: apps/transactions/filters.py:94
#: apps/transactions/filters.py:104
msgid "Reference date from"
msgstr "Data de Referência de"
#: apps/transactions/filters.py:104
#: apps/transactions/filters.py:114
msgid "Amount min"
msgstr "Quantia miníma"
#: apps/transactions/filters.py:109
#: apps/transactions/filters.py:119
msgid "Amount max"
msgstr "Quantia máxima"
#: apps/transactions/filters.py:185
#: apps/transactions/filters.py:202
msgid "Categorized"
msgstr "Categorizada"
#: apps/transactions/filters.py:192
#: apps/transactions/filters.py:209
msgid "Tagged"
msgstr "Com tag"
#: apps/transactions/filters.py:192
#: apps/transactions/filters.py:209
#: templates/insights/fragments/category_overview/index.html:189
#: templates/insights/fragments/month_by_month.html:121
#: templates/insights/fragments/year_by_year.html:75
msgid "Untagged"
msgstr "Sem tag"
#: apps/transactions/filters.py:198
#: apps/transactions/filters.py:215
msgid "Any entity"
msgstr "Qualquer entidade"
#: apps/transactions/filters.py:199
#: apps/transactions/filters.py:216
#: templates/insights/fragments/category_overview/index.html:282
#: templates/insights/fragments/month_by_month.html:123
#: templates/insights/fragments/year_by_year.html:77
msgid "No entity"
msgstr "Sem entidade"
@@ -1464,10 +1492,12 @@ msgstr ""
"transações"
#: apps/transactions/models.py:276
#: templates/insights/fragments/month_by_month.html:88
#: templates/insights/fragments/year_by_year.html:56
msgid "Entity"
msgstr "Entidade"
#: apps/transactions/models.py:288 apps/transactions/models.py:983
#: apps/transactions/models.py:288 apps/transactions/models.py:987
#: templates/calendar_view/fragments/list.html:42
#: templates/calendar_view/fragments/list.html:44
#: templates/calendar_view/fragments/list.html:52
@@ -1475,11 +1505,11 @@ msgstr "Entidade"
#: templates/cotton/ui/quick_transactions_buttons.html:10
#: templates/cotton/ui/transactions_fab.html:10
#: templates/insights/fragments/category_overview/index.html:87
#: templates/monthly_overview/fragments/monthly_summary.html:39
#: templates/monthly_overview/fragments/monthly_summary.html:41
msgid "Income"
msgstr "Renda"
#: apps/transactions/models.py:289 apps/transactions/models.py:984
#: apps/transactions/models.py:289 apps/transactions/models.py:988
#: templates/calendar_view/fragments/list.html:46
#: templates/calendar_view/fragments/list.html:48
#: templates/calendar_view/fragments/list.html:56
@@ -1490,11 +1520,11 @@ msgstr "Renda"
msgid "Expense"
msgstr "Despesa"
#: apps/transactions/models.py:344 apps/transactions/models.py:596
#: apps/transactions/models.py:344 apps/transactions/models.py:600
msgid "Installment Plan"
msgstr "Parcelamento"
#: apps/transactions/models.py:353 apps/transactions/models.py:820
#: apps/transactions/models.py:353 apps/transactions/models.py:824
msgid "Recurring Transaction"
msgstr "Transação Recorrente"
@@ -1506,113 +1536,113 @@ msgstr "Apagado"
msgid "Deleted At"
msgstr "Apagado Em"
#: apps/transactions/models.py:476 templates/tags/fragments/table.html:69
#: apps/transactions/models.py:480 templates/tags/fragments/table.html:69
msgid "No tags"
msgstr "Nenhuma tag"
#: apps/transactions/models.py:478
#: apps/transactions/models.py:482
msgid "No category"
msgstr "Sem categoria"
#: apps/transactions/models.py:480
#: apps/transactions/models.py:484
msgid "No description"
msgstr "Sem descrição"
#: apps/transactions/models.py:528 templates/includes/sidebar.html:57
#: apps/transactions/models.py:532 templates/includes/sidebar.html:57
msgid "Yearly"
msgstr "Anual"
#: apps/transactions/models.py:529 apps/users/models.py:464
#: apps/transactions/models.py:533 apps/users/models.py:464
#: templates/includes/sidebar.html:51
msgid "Monthly"
msgstr "Mensal"
#: apps/transactions/models.py:530
#: apps/transactions/models.py:534
msgid "Weekly"
msgstr "Semanal"
#: apps/transactions/models.py:531
#: apps/transactions/models.py:535
msgid "Daily"
msgstr "Diária"
#: apps/transactions/models.py:544
#: apps/transactions/models.py:548
msgid "Number of Installments"
msgstr "Número de Parcelas"
#: apps/transactions/models.py:549
#: apps/transactions/models.py:553
msgid "Installment Start"
msgstr "Parcela inicial"
#: apps/transactions/models.py:550
#: apps/transactions/models.py:554
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:555 apps/transactions/models.py:790
#: apps/transactions/models.py:559 apps/transactions/models.py:794
msgid "Start Date"
msgstr "Data de Início"
#: apps/transactions/models.py:559 apps/transactions/models.py:791
#: apps/transactions/models.py:563 apps/transactions/models.py:795
msgid "End Date"
msgstr "Data Final"
#: apps/transactions/models.py:564
#: apps/transactions/models.py:568
msgid "Recurrence"
msgstr "Recorrência"
#: apps/transactions/models.py:567
#: apps/transactions/models.py:571
msgid "Installment Amount"
msgstr "Valor da Parcela"
#: apps/transactions/models.py:586 apps/transactions/models.py:810
#: apps/transactions/models.py:590 apps/transactions/models.py:814
msgid "Add description to transactions"
msgstr "Adicionar descrição às transações"
#: apps/transactions/models.py:589 apps/transactions/models.py:813
#: apps/transactions/models.py:593 apps/transactions/models.py:817
msgid "Add notes to transactions"
msgstr "Adicionar notas às transações"
#: apps/transactions/models.py:749
#: apps/transactions/models.py:753
msgid "day(s)"
msgstr "dia(s)"
#: apps/transactions/models.py:750
#: apps/transactions/models.py:754
msgid "week(s)"
msgstr "semana(s)"
#: apps/transactions/models.py:751
#: apps/transactions/models.py:755
msgid "month(s)"
msgstr "mês(es)"
#: apps/transactions/models.py:752
#: apps/transactions/models.py:756
msgid "year(s)"
msgstr "ano(s)"
#: apps/transactions/models.py:754
#: apps/transactions/models.py:758
#: templates/recurring_transactions/fragments/list.html:18
msgid "Paused"
msgstr "Pausado"
#: apps/transactions/models.py:793
#: apps/transactions/models.py:797
msgid "Recurrence Type"
msgstr "Tipo de recorrência"
#: apps/transactions/models.py:796
#: apps/transactions/models.py:800
msgid "Recurrence Interval"
msgstr "Intervalo de recorrência"
#: apps/transactions/models.py:799
#: apps/transactions/models.py:803
msgid "Keep at most"
msgstr "Manter no máximo"
#: apps/transactions/models.py:803
#: apps/transactions/models.py:807
msgid "Last Generated Date"
msgstr "Última data gerada"
#: apps/transactions/models.py:806
#: apps/transactions/models.py:810
msgid "Last Generated Reference Date"
msgstr "Última data de referência gerada"
#: apps/transactions/models.py:1050
#: apps/transactions/models.py:1054
#: apps/transactions/views/quick_transactions.py:178
#: apps/transactions/views/quick_transactions.py:187
#: apps/transactions/views/quick_transactions.py:189
@@ -1621,7 +1651,7 @@ msgstr "Última data de referência gerada"
msgid "Quick Transaction"
msgstr "Transação Rápida"
#: apps/transactions/models.py:1051 templates/includes/sidebar.html:98
#: apps/transactions/models.py:1055 templates/includes/sidebar.html:98
#: templates/quick_transactions/pages/index.html:5
#: templates/quick_transactions/pages/index.html:15
msgid "Quick Transactions"
@@ -1727,7 +1757,7 @@ msgstr "Item apagado com sucesso"
#: apps/transactions/views/quick_transactions.py:156
#: apps/transactions/views/transactions.py:53
#: apps/transactions/views/transactions.py:228
#: apps/transactions/views/transactions.py:238
msgid "Transaction added successfully"
msgstr "Transação adicionada com sucesso"
@@ -1767,30 +1797,30 @@ msgstr "Tag atualizada com sucesso"
msgid "Tag deleted successfully"
msgstr "Tag apagada com sucesso"
#: apps/transactions/views/transactions.py:252
#: apps/transactions/views/transactions.py:262
msgid "Transaction updated successfully"
msgstr "Transação atualizada com sucesso"
#: apps/transactions/views/transactions.py:303
#: apps/transactions/views/transactions.py:313
#, python-format
msgid "%(count)s transaction updated successfully"
msgid_plural "%(count)s transactions updated successfully"
msgstr[0] "%(count)s transação atualizada com sucesso"
msgstr[1] "%(count)s transações atualizadas com sucesso"
#: apps/transactions/views/transactions.py:339
#: apps/transactions/views/transactions.py:349
msgid "Transaction duplicated successfully"
msgstr "Transação duplicada com sucesso"
#: apps/transactions/views/transactions.py:381
#: apps/transactions/views/transactions.py:391
msgid "Transaction deleted successfully"
msgstr "Transação apagada com sucesso"
#: apps/transactions/views/transactions.py:399
#: apps/transactions/views/transactions.py:409
msgid "Transaction restored successfully"
msgstr "Transação restaurada com sucesso"
#: apps/transactions/views/transactions.py:425
#: apps/transactions/views/transactions.py:435
msgid "Transfer added successfully"
msgstr "Transferência adicionada com sucesso"
@@ -1832,10 +1862,10 @@ msgid "This account is deactivated"
msgstr "Essa conta está desativada"
#: apps/users/forms.py:62 apps/users/forms.py:75 apps/users/forms.py:97
#: templates/monthly_overview/pages/overview.html:95
#: templates/monthly_overview/pages/overview.html:141
#: templates/monthly_overview/pages/overview.html:98
#: templates/monthly_overview/pages/overview.html:245
#: templates/transactions/pages/transactions.html:47
#: templates/transactions/pages/transactions.html:94
#: templates/transactions/pages/transactions.html:195
msgid "Default"
msgstr "Padrão"
@@ -2264,10 +2294,6 @@ msgstr "Adicionar categoria"
msgid "Edit category"
msgstr "Editar categoria"
#: templates/categories/fragments/table.html:18
msgid "Muted"
msgstr "Silenciada"
#: templates/categories/fragments/table.html:73
#: templates/insights/fragments/category_overview/index.html:552
msgid "No categories"
@@ -2286,9 +2312,9 @@ msgstr "Fechar"
#: templates/cotton/config/search.html:6
#: templates/import_app/fragments/profiles/list_presets.html:13
#: templates/monthly_overview/pages/overview.html:115
#: templates/monthly_overview/pages/overview.html:219
#: templates/rules/fragments/transaction_rule/dry_run/visual.html:57
#: templates/transactions/pages/transactions.html:67
#: templates/transactions/pages/transactions.html:168
msgid "Search"
msgstr "Buscar"
@@ -2516,8 +2542,8 @@ msgid "No entries for this DCA"
msgstr "Nenhuma entrada neste CMP"
#: templates/dca/fragments/strategy/details.html:120
#: templates/monthly_overview/fragments/list.html:33
#: templates/transactions/fragments/list_all.html:33
#: templates/monthly_overview/fragments/list.html:59
#: templates/transactions/fragments/list_all.html:59
msgid "Try adding one"
msgstr "Tente adicionar uma"
@@ -2643,7 +2669,7 @@ msgstr "Nenhuma taxa de câmbio"
#: templates/exchange_rates/fragments/table.html:56
#: templates/exchange_rates_services/fragments/table.html:57
#: templates/transactions/fragments/list_all.html:43
#: templates/transactions/fragments/list_all.html:70
msgid "Page navigation"
msgstr "Navegação por página"
@@ -2663,15 +2689,22 @@ msgstr "Alvos"
msgid "Last fetch"
msgstr "Última execução"
#: templates/exchange_rates_services/fragments/list.html:61
#: templates/exchange_rates_services/fragments/list.html:62
#, python-format
msgid "%(counter)s consecutive failure"
msgid_plural "%(counter)s consecutive failures"
msgstr[0] "%(counter)s falha consecutiva"
msgstr[1] "%(counter)s falhas consecutivas"
#: templates/exchange_rates_services/fragments/list.html:69
msgid "currencies"
msgstr "moedas"
#: templates/exchange_rates_services/fragments/list.html:61
#: templates/exchange_rates_services/fragments/list.html:69
msgid "accounts"
msgstr "contas"
#: templates/exchange_rates_services/fragments/list.html:69
#: templates/exchange_rates_services/fragments/list.html:77
msgid "No services configured"
msgstr "Nenhum serviço configurado"
@@ -2927,7 +2960,11 @@ msgid "Final total"
msgstr "Total final"
#: templates/insights/fragments/category_overview/index.html:89
#: templates/monthly_overview/fragments/monthly_summary.html:165
#: templates/insights/fragments/month_by_month.html:91
#: templates/insights/fragments/month_by_month.html:186
#: templates/insights/fragments/year_by_year.html:59
#: templates/insights/fragments/year_by_year.html:140
#: templates/monthly_overview/fragments/monthly_summary.html:167
msgid "Total"
msgstr "Total"
@@ -2971,6 +3008,63 @@ msgstr "Nenhuma transação atrasada"
msgid "No recent transactions"
msgstr "Nenhuma transação recente"
#: templates/insights/fragments/month_by_month.html:86
#: templates/insights/fragments/year_by_year.html:54
msgid "Tag"
msgstr "Tag"
#: templates/insights/fragments/month_by_month.html:94
msgid "Jan"
msgstr "Jan"
#: templates/insights/fragments/month_by_month.html:95
msgid "Feb"
msgstr "Fev"
#: templates/insights/fragments/month_by_month.html:96
msgid "Mar"
msgstr "Mar"
#: templates/insights/fragments/month_by_month.html:97
msgid "Apr"
msgstr "Abr"
#: templates/insights/fragments/month_by_month.html:98
msgid "May"
msgstr "Mai"
#: templates/insights/fragments/month_by_month.html:99
msgid "Jun"
msgstr "Jun"
#: templates/insights/fragments/month_by_month.html:100
msgid "Jul"
msgstr "Jul"
#: templates/insights/fragments/month_by_month.html:101
msgid "Aug"
msgstr "Ago"
#: templates/insights/fragments/month_by_month.html:102
msgid "Sep"
msgstr "Set"
#: templates/insights/fragments/month_by_month.html:103
msgid "Oct"
msgstr "Out"
#: templates/insights/fragments/month_by_month.html:104
msgid "Nov"
msgstr "Nov"
#: templates/insights/fragments/month_by_month.html:105
msgid "Dec"
msgstr "Dez"
#: templates/insights/fragments/month_by_month.html:248
msgid "No transactions for this year"
msgstr "Nenhuma transação neste ano"
#: templates/insights/fragments/sankey.html:100
msgid "From"
msgstr "De"
@@ -2979,6 +3073,10 @@ msgstr "De"
msgid "Percentage"
msgstr "Porcentagem"
#: templates/insights/fragments/year_by_year.html:202
msgid "No transactions"
msgstr "Sem transações"
#: templates/insights/pages/index.html:37
msgid "Month"
msgstr "Mês"
@@ -3028,6 +3126,14 @@ msgstr "Últimas Transações"
msgid "Emergency Fund"
msgstr "Reserva de Emergência"
#: templates/insights/pages/index.html:127
msgid "Year by Year"
msgstr "Ano a ano"
#: templates/insights/pages/index.html:132
msgid "Month by Month"
msgstr "Mês a Mês"
#: templates/installment_plans/fragments/add.html:5
msgid "Add installment plan"
msgstr "Adicionar parcelamento"
@@ -3102,35 +3208,40 @@ msgstr "Preço unitário"
msgid "Item"
msgstr "Item"
#: templates/monthly_overview/fragments/list.html:32
#: templates/monthly_overview/fragments/list.html:15
#: templates/transactions/fragments/list_all.html:15
msgid "late"
msgstr "atrasado"
#: templates/monthly_overview/fragments/list.html:58
msgid "No transactions this month"
msgstr "Nenhuma transação neste mês"
#: templates/monthly_overview/fragments/monthly_summary.html:6
#: templates/monthly_overview/fragments/monthly_summary.html:7
msgid "Daily Spending Allowance"
msgstr "Gasto Diário"
#: templates/monthly_overview/fragments/monthly_summary.html:6
#: templates/monthly_overview/fragments/monthly_summary.html:7
msgid "This is the final total divided by the remaining days in the month"
msgstr "Esse é o total final dividido pelos dias restantes do mês"
#: templates/monthly_overview/fragments/monthly_summary.html:42
#: templates/monthly_overview/fragments/monthly_summary.html:105
#: templates/monthly_overview/fragments/monthly_summary.html:168
#: templates/monthly_overview/fragments/monthly_summary.html:44
#: templates/monthly_overview/fragments/monthly_summary.html:107
#: templates/monthly_overview/fragments/monthly_summary.html:170
msgid "current"
msgstr "atual"
#: templates/monthly_overview/fragments/monthly_summary.html:71
#: templates/monthly_overview/fragments/monthly_summary.html:134
#: templates/monthly_overview/fragments/monthly_summary.html:197
#: templates/monthly_overview/fragments/monthly_summary.html:73
#: templates/monthly_overview/fragments/monthly_summary.html:136
#: templates/monthly_overview/fragments/monthly_summary.html:199
msgid "projected"
msgstr "previsto"
#: templates/monthly_overview/fragments/monthly_summary.html:102
#: templates/monthly_overview/fragments/monthly_summary.html:104
msgid "Expenses"
msgstr "Despesas"
#: templates/monthly_overview/fragments/monthly_summary.html:255
#: templates/monthly_overview/fragments/monthly_summary.html:257
msgid "Distribution"
msgstr "Distribuição"
@@ -3138,27 +3249,27 @@ msgstr "Distribuição"
msgid "Summary"
msgstr "Resumo"
#: templates/monthly_overview/pages/overview.html:96
#: templates/monthly_overview/pages/overview.html:150
#: templates/monthly_overview/pages/overview.html:99
#: templates/monthly_overview/pages/overview.html:254
#: templates/transactions/pages/transactions.html:48
#: templates/transactions/pages/transactions.html:103
#: templates/transactions/pages/transactions.html:204
msgid "Oldest first"
msgstr "Mais antigas primeiro"
#: templates/monthly_overview/pages/overview.html:97
#: templates/monthly_overview/pages/overview.html:159
#: templates/monthly_overview/pages/overview.html:100
#: templates/monthly_overview/pages/overview.html:263
#: templates/transactions/pages/transactions.html:49
#: templates/transactions/pages/transactions.html:112
#: templates/transactions/pages/transactions.html:213
msgid "Newest first"
msgstr "Mais novas primeiro"
#: templates/monthly_overview/pages/overview.html:106
#: templates/monthly_overview/pages/overview.html:109
#: templates/transactions/pages/transactions.html:58
msgid "Filter transactions"
msgstr "Filtrar transações"
#: templates/monthly_overview/pages/overview.html:131
#: templates/transactions/pages/transactions.html:84
#: templates/monthly_overview/pages/overview.html:235
#: templates/transactions/pages/transactions.html:185
msgid "Order by"
msgstr "Ordernar por"
@@ -3402,7 +3513,7 @@ msgstr "Editando"
msgid "transactions"
msgstr "transações"
#: templates/transactions/fragments/list_all.html:32
#: templates/transactions/fragments/list_all.html:58
msgid "No transactions found"
msgstr "Nenhuma transação encontrada"

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-12-20 02:59+0000\n"
"POT-Creation-Date: 2026-01-10 20:50+0000\n"
"PO-Revision-Date: 2025-04-14 06:16+0000\n"
"Last-Translator: Emil <emil.bjorkroth@gmail.com>\n"
"Language-Team: Swedish <https://translations.herculino.com/projects/wygiwyh/"
@@ -67,24 +67,30 @@ msgstr ""
#: apps/transactions/forms.py:419 apps/transactions/forms.py:516
#: apps/transactions/forms.py:523 apps/transactions/forms.py:707
#: apps/transactions/forms.py:948 apps/transactions/models.py:322
#: apps/transactions/models.py:574 apps/transactions/models.py:774
#: apps/transactions/models.py:1022
#: apps/transactions/models.py:578 apps/transactions/models.py:778
#: apps/transactions/models.py:1026
#: templates/insights/fragments/category_overview/index.html:86
#: templates/insights/fragments/category_overview/index.html:542
#: templates/insights/fragments/month_by_month.html:84
#: templates/insights/fragments/year_by_year.html:52
msgid "Category"
msgstr ""
#: apps/accounts/forms.py:132 apps/dca/forms.py:95 apps/dca/forms.py:103
#: apps/export_app/forms.py:43 apps/export_app/forms.py:132
#: apps/rules/forms.py:184 apps/rules/forms.py:194 apps/rules/models.py:45
#: apps/rules/models.py:315 apps/transactions/filters.py:68
#: apps/rules/models.py:315 apps/transactions/filters.py:73
#: apps/transactions/forms.py:51 apps/transactions/forms.py:259
#: apps/transactions/forms.py:427 apps/transactions/forms.py:532
#: apps/transactions/forms.py:540 apps/transactions/forms.py:700
#: apps/transactions/forms.py:941 apps/transactions/models.py:328
#: apps/transactions/models.py:576 apps/transactions/models.py:778
#: apps/transactions/models.py:1028 templates/includes/sidebar.html:150
#: apps/transactions/models.py:580 apps/transactions/models.py:782
#: apps/transactions/models.py:1032 templates/includes/sidebar.html:150
#: templates/insights/fragments/category_overview/index.html:40
#: templates/insights/fragments/month_by_month.html:29
#: templates/insights/fragments/month_by_month.html:32
#: templates/insights/fragments/year_by_year.html:24
#: templates/insights/fragments/year_by_year.html:27
#: templates/tags/fragments/list.html:9 templates/tags/pages/index.html:4
msgid "Tags"
msgstr ""
@@ -92,7 +98,7 @@ msgstr ""
#: apps/accounts/models.py:12 apps/accounts/models.py:29 apps/dca/models.py:13
#: apps/import_app/models.py:14 apps/rules/models.py:13
#: apps/transactions/models.py:214 apps/transactions/models.py:239
#: apps/transactions/models.py:263 apps/transactions/models.py:990
#: apps/transactions/models.py:263 apps/transactions/models.py:994
#: templates/account_groups/fragments/list.html:22
#: templates/accounts/fragments/list.html:22
#: templates/categories/fragments/table.html:17
@@ -161,8 +167,8 @@ msgstr ""
#: apps/transactions/forms.py:63 apps/transactions/forms.py:271
#: apps/transactions/forms.py:386 apps/transactions/forms.py:692
#: apps/transactions/forms.py:933 apps/transactions/models.py:294
#: apps/transactions/models.py:534 apps/transactions/models.py:756
#: apps/transactions/models.py:996
#: apps/transactions/models.py:538 apps/transactions/models.py:760
#: apps/transactions/models.py:1000
#: templates/installment_plans/fragments/table.html:17
#: templates/quick_transactions/fragments/list.html:14
#: templates/recurring_transactions/fragments/table.html:19
@@ -171,11 +177,11 @@ msgid "Account"
msgstr ""
#: apps/accounts/models.py:76 apps/export_app/forms.py:19
#: apps/export_app/forms.py:129 apps/transactions/filters.py:52
#: apps/export_app/forms.py:129 apps/transactions/filters.py:57
#: templates/accounts/fragments/list.html:9
#: templates/accounts/pages/index.html:4 templates/includes/sidebar.html:162
#: templates/includes/sidebar.html:164
#: templates/monthly_overview/pages/overview.html:75
#: templates/monthly_overview/pages/overview.html:77
#: templates/transactions/pages/transactions.html:26
msgid "Accounts"
msgstr ""
@@ -190,8 +196,8 @@ msgstr ""
#: apps/accounts/views/account_groups.py:69
#: apps/accounts/views/account_groups.py:152 apps/accounts/views/accounts.py:68
#: apps/accounts/views/accounts.py:106 apps/dca/views.py:63
#: apps/dca/views.py:146 apps/rules/views.py:118 apps/rules/views.py:228
#: apps/accounts/views/accounts.py:106 apps/dca/views.py:62
#: apps/dca/views.py:145 apps/rules/views.py:118 apps/rules/views.py:228
#: apps/transactions/views/categories.py:91
#: apps/transactions/views/categories.py:129
#: apps/transactions/views/entities.py:91
@@ -205,7 +211,7 @@ msgid "Account Group updated successfully"
msgstr ""
#: apps/accounts/views/account_groups.py:111
#: apps/accounts/views/accounts.py:145 apps/dca/views.py:105
#: apps/accounts/views/accounts.py:145 apps/dca/views.py:104
#: apps/rules/views.py:185 apps/transactions/views/categories.py:168
#: apps/transactions/views/entities.py:130 apps/transactions/views/tags.py:130
msgid "Item no longer shared with you"
@@ -216,14 +222,14 @@ msgid "Account Group deleted successfully"
msgstr ""
#: apps/accounts/views/account_groups.py:135
#: apps/accounts/views/accounts.py:189 apps/dca/views.py:129
#: apps/accounts/views/accounts.py:189 apps/dca/views.py:128
#: apps/rules/views.py:210 apps/transactions/views/categories.py:192
#: apps/transactions/views/entities.py:154 apps/transactions/views/tags.py:154
msgid "Ownership taken successfully"
msgstr ""
#: apps/accounts/views/account_groups.py:165
#: apps/accounts/views/accounts.py:119 apps/dca/views.py:159
#: apps/accounts/views/accounts.py:119 apps/dca/views.py:158
#: apps/rules/views.py:241 apps/transactions/views/categories.py:142
#: apps/transactions/views/entities.py:184 apps/transactions/views/tags.py:184
msgid "Configuration saved successfully"
@@ -356,7 +362,7 @@ msgid "Public"
msgstr ""
#: apps/common/templatetags/natural.py:20
#: templates/monthly_overview/fragments/monthly_summary.html:9
#: templates/monthly_overview/fragments/monthly_summary.html:10
msgid "today"
msgstr ""
@@ -454,10 +460,10 @@ msgstr ""
#: apps/common/widgets/tom_select.py:15
#: templates/mini_tools/unit_price_calculator.html:180
#: templates/monthly_overview/pages/overview.html:171
#: templates/monthly_overview/pages/overview.html:183
#: templates/transactions/pages/transactions.html:124
#: templates/transactions/pages/transactions.html:136
#: templates/monthly_overview/pages/overview.html:275
#: templates/monthly_overview/pages/overview.html:287
#: templates/transactions/pages/transactions.html:225
#: templates/transactions/pages/transactions.html:237
msgid "Clear"
msgstr ""
@@ -496,11 +502,11 @@ msgid "Decimal Places"
msgstr ""
#: apps/currencies/models.py:45 apps/export_app/forms.py:25
#: apps/export_app/forms.py:130 apps/transactions/filters.py:59
#: apps/export_app/forms.py:130 apps/transactions/filters.py:64
#: templates/currencies/fragments/list.html:9
#: templates/currencies/pages/index.html:4 templates/includes/sidebar.html:176
#: templates/includes/sidebar.html:178
#: templates/monthly_overview/pages/overview.html:62
#: templates/monthly_overview/pages/overview.html:63
#: templates/transactions/pages/transactions.html:12
msgid "Currencies"
msgstr ""
@@ -561,9 +567,9 @@ msgstr ""
msgid "Service Type"
msgstr ""
#: apps/currencies/models.py:118 apps/transactions/models.py:218
#: apps/transactions/models.py:242 apps/transactions/models.py:266
#: templates/categories/fragments/list.html:16
#: apps/currencies/models.py:118 apps/transactions/filters.py:27
#: apps/transactions/models.py:218 apps/transactions/models.py:242
#: apps/transactions/models.py:266 templates/categories/fragments/list.html:16
#: templates/entities/fragments/list.html:16
#: templates/installment_plans/fragments/list.html:16
#: templates/recurring_transactions/fragments/list.html:16
@@ -591,57 +597,57 @@ msgstr ""
msgid "Last Successful Fetch"
msgstr ""
#: apps/currencies/models.py:141
#: apps/currencies/models.py:143
msgid "Target Currencies"
msgstr ""
#: apps/currencies/models.py:143
#: apps/currencies/models.py:145
msgid ""
"Select currencies to fetch exchange rates for. Rates will be fetched for "
"each currency against their set exchange currency."
msgstr ""
#: apps/currencies/models.py:151
#: apps/currencies/models.py:153
msgid "Target Accounts"
msgstr ""
#: apps/currencies/models.py:153
#: apps/currencies/models.py:155
msgid ""
"Select accounts to fetch exchange rates for. Rates will be fetched for each "
"account's currency against their set exchange currency."
msgstr ""
#: apps/currencies/models.py:160
#: apps/currencies/models.py:162
msgid "Single exchange rate"
msgstr ""
#: apps/currencies/models.py:163
#: apps/currencies/models.py:165
msgid "Create one exchange rate and keep updating it. Avoids database clutter."
msgstr ""
#: apps/currencies/models.py:168
#: apps/currencies/models.py:170
msgid "Exchange Rate Service"
msgstr ""
#: apps/currencies/models.py:169
#: apps/currencies/models.py:171
msgid "Exchange Rate Services"
msgstr ""
#: apps/currencies/models.py:221
#: apps/currencies/models.py:223
msgid "'Every X hours' interval type requires a positive integer."
msgstr ""
#: apps/currencies/models.py:230
#: apps/currencies/models.py:232
msgid "'Every X hours' interval must be between 1 and 24."
msgstr ""
#: apps/currencies/models.py:244
#: apps/currencies/models.py:246
msgid ""
"Invalid hour format. Use comma-separated hours (0-23) and/or ranges (e.g., "
"'1-5,8,10-12')."
msgstr ""
#: apps/currencies/models.py:255
#: apps/currencies/models.py:257
msgid ""
"Invalid format. Please check the requirements for your selected interval "
"type."
@@ -740,8 +746,8 @@ msgstr ""
#: apps/dca/models.py:26 apps/dca/models.py:181 apps/rules/forms.py:180
#: apps/rules/forms.py:196 apps/rules/models.py:43 apps/rules/models.py:295
#: apps/transactions/forms.py:413 apps/transactions/forms.py:560
#: apps/transactions/models.py:318 apps/transactions/models.py:583
#: apps/transactions/models.py:784 apps/transactions/models.py:1018
#: apps/transactions/models.py:318 apps/transactions/models.py:587
#: apps/transactions/models.py:788 apps/transactions/models.py:1022
msgid "Notes"
msgstr ""
@@ -773,27 +779,27 @@ msgstr ""
msgid "DCA Entries"
msgstr ""
#: apps/dca/views.py:39
#: apps/dca/views.py:38
msgid "DCA Strategy added successfully"
msgstr ""
#: apps/dca/views.py:76
#: apps/dca/views.py:75
msgid "DCA Strategy updated successfully"
msgstr ""
#: apps/dca/views.py:108
#: apps/dca/views.py:107
msgid "DCA strategy deleted successfully"
msgstr ""
#: apps/dca/views.py:238
#: apps/dca/views.py:237
msgid "Entry added successfully"
msgstr ""
#: apps/dca/views.py:265
#: apps/dca/views.py:264
msgid "Entry updated successfully"
msgstr ""
#: apps/dca/views.py:291
#: apps/dca/views.py:290
msgid "Entry deleted successfully"
msgstr ""
@@ -813,34 +819,42 @@ msgid "Transactions"
msgstr ""
#: apps/export_app/forms.py:37 apps/export_app/forms.py:131
#: apps/transactions/filters.py:63 templates/categories/fragments/list.html:9
#: apps/transactions/filters.py:68 templates/categories/fragments/list.html:9
#: templates/categories/pages/index.html:4 templates/includes/sidebar.html:144
#: templates/insights/fragments/month_by_month.html:18
#: templates/insights/fragments/month_by_month.html:21
#: templates/insights/fragments/year_by_year.html:13
#: templates/insights/fragments/year_by_year.html:16
msgid "Categories"
msgstr ""
#: apps/export_app/forms.py:49 apps/export_app/forms.py:133
#: apps/rules/forms.py:185 apps/rules/forms.py:195 apps/rules/models.py:46
#: apps/rules/models.py:307 apps/transactions/filters.py:73
#: apps/rules/models.py:307 apps/transactions/filters.py:78
#: apps/transactions/forms.py:59 apps/transactions/forms.py:267
#: apps/transactions/forms.py:435 apps/transactions/forms.py:715
#: apps/transactions/forms.py:956 apps/transactions/models.py:277
#: apps/transactions/models.py:333 apps/transactions/models.py:579
#: apps/transactions/models.py:781 apps/transactions/models.py:1033
#: apps/transactions/models.py:333 apps/transactions/models.py:583
#: apps/transactions/models.py:785 apps/transactions/models.py:1037
#: templates/entities/fragments/list.html:9
#: templates/entities/pages/index.html:4 templates/includes/sidebar.html:156
#: templates/insights/fragments/category_overview/index.html:54
#: templates/insights/fragments/month_by_month.html:40
#: templates/insights/fragments/month_by_month.html:43
#: templates/insights/fragments/year_by_year.html:35
#: templates/insights/fragments/year_by_year.html:38
msgid "Entities"
msgstr ""
#: apps/export_app/forms.py:55 apps/export_app/forms.py:137
#: apps/transactions/models.py:821 templates/includes/sidebar.html:110
#: apps/transactions/models.py:825 templates/includes/sidebar.html:110
#: templates/recurring_transactions/fragments/list.html:9
#: templates/recurring_transactions/pages/index.html:4
msgid "Recurring Transactions"
msgstr ""
#: apps/export_app/forms.py:61 apps/export_app/forms.py:135
#: apps/transactions/models.py:597 templates/includes/sidebar.html:104
#: apps/transactions/models.py:601 templates/includes/sidebar.html:104
#: templates/installment_plans/fragments/list.html:9
#: templates/installment_plans/pages/index.html:4
msgid "Installment Plans"
@@ -992,10 +1006,12 @@ msgid "Run deleted successfully"
msgstr ""
#: apps/insights/forms.py:118 apps/insights/utils/sankey.py:36
#: apps/insights/utils/sankey.py:167 apps/transactions/filters.py:186
#: apps/insights/utils/sankey.py:167 apps/transactions/filters.py:203
#: templates/insights/fragments/category_overview/index.html:96
#: templates/insights/fragments/category_overview/index.html:407
#: templates/insights/fragments/category_overview/index.html:436
#: templates/insights/fragments/month_by_month.html:119
#: templates/insights/fragments/year_by_year.html:73
msgid "Uncategorized"
msgstr ""
@@ -1088,15 +1104,15 @@ msgstr ""
#: apps/rules/forms.py:174 apps/rules/forms.py:188 apps/rules/models.py:36
#: apps/rules/models.py:271 apps/transactions/forms.py:377
#: apps/transactions/models.py:301 apps/transactions/models.py:539
#: apps/transactions/models.py:762 apps/transactions/models.py:1003
#: apps/transactions/models.py:301 apps/transactions/models.py:543
#: apps/transactions/models.py:766 apps/transactions/models.py:1007
msgid "Type"
msgstr ""
#: apps/rules/forms.py:175 apps/rules/forms.py:189 apps/rules/models.py:37
#: apps/rules/models.py:275 apps/transactions/filters.py:22
#: apps/transactions/forms.py:381 apps/transactions/models.py:303
#: apps/transactions/models.py:1005 templates/cotton/transaction/item.html:20
#: apps/transactions/models.py:1009 templates/cotton/transaction/item.html:20
#: templates/cotton/transaction/item.html:31
#: templates/transactions/widgets/paid_toggle_button.html:10
#: templates/transactions/widgets/unselectable_paid_toggle_button.html:13
@@ -1107,14 +1123,14 @@ msgstr ""
#: apps/rules/models.py:283 apps/transactions/forms.py:71
#: apps/transactions/forms.py:397 apps/transactions/forms.py:547
#: apps/transactions/forms.py:721 apps/transactions/models.py:305
#: apps/transactions/models.py:557 apps/transactions/models.py:786
#: apps/transactions/models.py:561 apps/transactions/models.py:790
msgid "Reference Date"
msgstr ""
#: apps/rules/forms.py:178 apps/rules/forms.py:192 apps/rules/models.py:41
#: apps/rules/models.py:287 apps/transactions/forms.py:404
#: apps/transactions/models.py:311 apps/transactions/models.py:767
#: apps/transactions/models.py:1011
#: apps/transactions/models.py:311 apps/transactions/models.py:771
#: apps/transactions/models.py:1015
#: templates/insights/fragments/sankey.html:102
#: templates/installment_plans/fragments/table.html:18
#: templates/quick_transactions/fragments/list.html:15
@@ -1125,27 +1141,27 @@ msgstr ""
#: apps/rules/forms.py:179 apps/rules/forms.py:193 apps/rules/models.py:14
#: apps/rules/models.py:42 apps/rules/models.py:291
#: apps/transactions/forms.py:408 apps/transactions/forms.py:551
#: apps/transactions/models.py:316 apps/transactions/models.py:541
#: apps/transactions/models.py:770 apps/transactions/models.py:1016
#: apps/transactions/models.py:316 apps/transactions/models.py:545
#: apps/transactions/models.py:774 apps/transactions/models.py:1020
msgid "Description"
msgstr ""
#: apps/rules/forms.py:182 apps/rules/forms.py:198 apps/rules/models.py:47
#: apps/rules/models.py:299 apps/transactions/models.py:355
#: apps/transactions/models.py:1038
#: apps/transactions/models.py:1042
msgid "Internal Note"
msgstr ""
#: apps/rules/forms.py:183 apps/rules/forms.py:199 apps/rules/models.py:48
#: apps/rules/models.py:303 apps/transactions/models.py:357
#: apps/transactions/models.py:1040
#: apps/transactions/models.py:1044
msgid "Internal ID"
msgstr ""
#: apps/rules/forms.py:186 apps/rules/forms.py:200 apps/rules/models.py:40
#: apps/rules/models.py:319 apps/transactions/forms.py:564
#: apps/transactions/models.py:215 apps/transactions/models.py:306
#: apps/transactions/models.py:1006
#: apps/transactions/models.py:1010
msgid "Mute"
msgstr ""
@@ -1301,53 +1317,65 @@ msgstr ""
msgid "Projected"
msgstr ""
#: apps/transactions/filters.py:40
#: apps/transactions/filters.py:28 templates/categories/fragments/table.html:18
msgid "Muted"
msgstr ""
#: apps/transactions/filters.py:45
msgid "Content"
msgstr ""
#: apps/transactions/filters.py:46
#: apps/transactions/filters.py:51
msgid "Transaction Type"
msgstr ""
#: apps/transactions/filters.py:84
msgid "Date from"
msgstr ""
#: apps/transactions/filters.py:89 apps/transactions/filters.py:99
msgid "Until"
#: apps/transactions/filters.py:89
msgid "Mute Status"
msgstr ""
#: apps/transactions/filters.py:94
msgid "Reference date from"
msgid "Date from"
msgstr ""
#: apps/transactions/filters.py:99 apps/transactions/filters.py:109
msgid "Until"
msgstr ""
#: apps/transactions/filters.py:104
msgid "Reference date from"
msgstr ""
#: apps/transactions/filters.py:114
msgid "Amount min"
msgstr ""
#: apps/transactions/filters.py:109
#: apps/transactions/filters.py:119
msgid "Amount max"
msgstr ""
#: apps/transactions/filters.py:185
#: apps/transactions/filters.py:202
msgid "Categorized"
msgstr ""
#: apps/transactions/filters.py:192
#: apps/transactions/filters.py:209
msgid "Tagged"
msgstr ""
#: apps/transactions/filters.py:192
#: apps/transactions/filters.py:209
#: templates/insights/fragments/category_overview/index.html:189
#: templates/insights/fragments/month_by_month.html:121
#: templates/insights/fragments/year_by_year.html:75
msgid "Untagged"
msgstr ""
#: apps/transactions/filters.py:198
#: apps/transactions/filters.py:215
msgid "Any entity"
msgstr ""
#: apps/transactions/filters.py:199
#: apps/transactions/filters.py:216
#: templates/insights/fragments/category_overview/index.html:282
#: templates/insights/fragments/month_by_month.html:123
#: templates/insights/fragments/year_by_year.html:77
msgid "No entity"
msgstr ""
@@ -1435,10 +1463,12 @@ msgid ""
msgstr ""
#: apps/transactions/models.py:276
#: templates/insights/fragments/month_by_month.html:88
#: templates/insights/fragments/year_by_year.html:56
msgid "Entity"
msgstr ""
#: apps/transactions/models.py:288 apps/transactions/models.py:983
#: apps/transactions/models.py:288 apps/transactions/models.py:987
#: templates/calendar_view/fragments/list.html:42
#: templates/calendar_view/fragments/list.html:44
#: templates/calendar_view/fragments/list.html:52
@@ -1446,11 +1476,11 @@ msgstr ""
#: templates/cotton/ui/quick_transactions_buttons.html:10
#: templates/cotton/ui/transactions_fab.html:10
#: templates/insights/fragments/category_overview/index.html:87
#: templates/monthly_overview/fragments/monthly_summary.html:39
#: templates/monthly_overview/fragments/monthly_summary.html:41
msgid "Income"
msgstr ""
#: apps/transactions/models.py:289 apps/transactions/models.py:984
#: apps/transactions/models.py:289 apps/transactions/models.py:988
#: templates/calendar_view/fragments/list.html:46
#: templates/calendar_view/fragments/list.html:48
#: templates/calendar_view/fragments/list.html:56
@@ -1461,11 +1491,11 @@ msgstr ""
msgid "Expense"
msgstr ""
#: apps/transactions/models.py:344 apps/transactions/models.py:596
#: apps/transactions/models.py:344 apps/transactions/models.py:600
msgid "Installment Plan"
msgstr ""
#: apps/transactions/models.py:353 apps/transactions/models.py:820
#: apps/transactions/models.py:353 apps/transactions/models.py:824
msgid "Recurring Transaction"
msgstr ""
@@ -1477,113 +1507,113 @@ msgstr ""
msgid "Deleted At"
msgstr ""
#: apps/transactions/models.py:476 templates/tags/fragments/table.html:69
#: apps/transactions/models.py:480 templates/tags/fragments/table.html:69
msgid "No tags"
msgstr ""
#: apps/transactions/models.py:478
#: apps/transactions/models.py:482
msgid "No category"
msgstr ""
#: apps/transactions/models.py:480
#: apps/transactions/models.py:484
msgid "No description"
msgstr ""
#: apps/transactions/models.py:528 templates/includes/sidebar.html:57
#: apps/transactions/models.py:532 templates/includes/sidebar.html:57
msgid "Yearly"
msgstr ""
#: apps/transactions/models.py:529 apps/users/models.py:464
#: apps/transactions/models.py:533 apps/users/models.py:464
#: templates/includes/sidebar.html:51
msgid "Monthly"
msgstr ""
#: apps/transactions/models.py:530
#: apps/transactions/models.py:534
msgid "Weekly"
msgstr ""
#: apps/transactions/models.py:531
#: apps/transactions/models.py:535
msgid "Daily"
msgstr ""
#: apps/transactions/models.py:544
#: apps/transactions/models.py:548
msgid "Number of Installments"
msgstr ""
#: apps/transactions/models.py:549
#: apps/transactions/models.py:553
msgid "Installment Start"
msgstr ""
#: apps/transactions/models.py:550
#: apps/transactions/models.py:554
msgid "The installment number to start counting from"
msgstr ""
#: apps/transactions/models.py:555 apps/transactions/models.py:790
#: apps/transactions/models.py:559 apps/transactions/models.py:794
msgid "Start Date"
msgstr ""
#: apps/transactions/models.py:559 apps/transactions/models.py:791
#: apps/transactions/models.py:563 apps/transactions/models.py:795
msgid "End Date"
msgstr ""
#: apps/transactions/models.py:564
#: apps/transactions/models.py:568
msgid "Recurrence"
msgstr ""
#: apps/transactions/models.py:567
#: apps/transactions/models.py:571
msgid "Installment Amount"
msgstr ""
#: apps/transactions/models.py:586 apps/transactions/models.py:810
#: apps/transactions/models.py:590 apps/transactions/models.py:814
msgid "Add description to transactions"
msgstr ""
#: apps/transactions/models.py:589 apps/transactions/models.py:813
#: apps/transactions/models.py:593 apps/transactions/models.py:817
msgid "Add notes to transactions"
msgstr ""
#: apps/transactions/models.py:749
#: apps/transactions/models.py:753
msgid "day(s)"
msgstr ""
#: apps/transactions/models.py:750
#: apps/transactions/models.py:754
msgid "week(s)"
msgstr ""
#: apps/transactions/models.py:751
#: apps/transactions/models.py:755
msgid "month(s)"
msgstr ""
#: apps/transactions/models.py:752
#: apps/transactions/models.py:756
msgid "year(s)"
msgstr ""
#: apps/transactions/models.py:754
#: apps/transactions/models.py:758
#: templates/recurring_transactions/fragments/list.html:18
msgid "Paused"
msgstr ""
#: apps/transactions/models.py:793
#: apps/transactions/models.py:797
msgid "Recurrence Type"
msgstr ""
#: apps/transactions/models.py:796
#: apps/transactions/models.py:800
msgid "Recurrence Interval"
msgstr ""
#: apps/transactions/models.py:799
#: apps/transactions/models.py:803
msgid "Keep at most"
msgstr ""
#: apps/transactions/models.py:803
#: apps/transactions/models.py:807
msgid "Last Generated Date"
msgstr ""
#: apps/transactions/models.py:806
#: apps/transactions/models.py:810
msgid "Last Generated Reference Date"
msgstr ""
#: apps/transactions/models.py:1050
#: apps/transactions/models.py:1054
#: apps/transactions/views/quick_transactions.py:178
#: apps/transactions/views/quick_transactions.py:187
#: apps/transactions/views/quick_transactions.py:189
@@ -1592,7 +1622,7 @@ msgstr ""
msgid "Quick Transaction"
msgstr ""
#: apps/transactions/models.py:1051 templates/includes/sidebar.html:98
#: apps/transactions/models.py:1055 templates/includes/sidebar.html:98
#: templates/quick_transactions/pages/index.html:5
#: templates/quick_transactions/pages/index.html:15
msgid "Quick Transactions"
@@ -1698,7 +1728,7 @@ msgstr ""
#: apps/transactions/views/quick_transactions.py:156
#: apps/transactions/views/transactions.py:53
#: apps/transactions/views/transactions.py:228
#: apps/transactions/views/transactions.py:238
msgid "Transaction added successfully"
msgstr ""
@@ -1738,30 +1768,30 @@ msgstr ""
msgid "Tag deleted successfully"
msgstr ""
#: apps/transactions/views/transactions.py:252
#: apps/transactions/views/transactions.py:262
msgid "Transaction updated successfully"
msgstr ""
#: apps/transactions/views/transactions.py:303
#: apps/transactions/views/transactions.py:313
#, python-format
msgid "%(count)s transaction updated successfully"
msgid_plural "%(count)s transactions updated successfully"
msgstr[0] ""
msgstr[1] ""
#: apps/transactions/views/transactions.py:339
#: apps/transactions/views/transactions.py:349
msgid "Transaction duplicated successfully"
msgstr ""
#: apps/transactions/views/transactions.py:381
#: apps/transactions/views/transactions.py:391
msgid "Transaction deleted successfully"
msgstr ""
#: apps/transactions/views/transactions.py:399
#: apps/transactions/views/transactions.py:409
msgid "Transaction restored successfully"
msgstr ""
#: apps/transactions/views/transactions.py:425
#: apps/transactions/views/transactions.py:435
msgid "Transfer added successfully"
msgstr ""
@@ -1803,10 +1833,10 @@ msgid "This account is deactivated"
msgstr ""
#: apps/users/forms.py:62 apps/users/forms.py:75 apps/users/forms.py:97
#: templates/monthly_overview/pages/overview.html:95
#: templates/monthly_overview/pages/overview.html:141
#: templates/monthly_overview/pages/overview.html:98
#: templates/monthly_overview/pages/overview.html:245
#: templates/transactions/pages/transactions.html:47
#: templates/transactions/pages/transactions.html:94
#: templates/transactions/pages/transactions.html:195
msgid "Default"
msgstr ""
@@ -2226,10 +2256,6 @@ msgstr ""
msgid "Edit category"
msgstr ""
#: templates/categories/fragments/table.html:18
msgid "Muted"
msgstr ""
#: templates/categories/fragments/table.html:73
#: templates/insights/fragments/category_overview/index.html:552
msgid "No categories"
@@ -2248,9 +2274,9 @@ msgstr ""
#: templates/cotton/config/search.html:6
#: templates/import_app/fragments/profiles/list_presets.html:13
#: templates/monthly_overview/pages/overview.html:115
#: templates/monthly_overview/pages/overview.html:219
#: templates/rules/fragments/transaction_rule/dry_run/visual.html:57
#: templates/transactions/pages/transactions.html:67
#: templates/transactions/pages/transactions.html:168
msgid "Search"
msgstr ""
@@ -2478,8 +2504,8 @@ msgid "No entries for this DCA"
msgstr ""
#: templates/dca/fragments/strategy/details.html:120
#: templates/monthly_overview/fragments/list.html:33
#: templates/transactions/fragments/list_all.html:33
#: templates/monthly_overview/fragments/list.html:59
#: templates/transactions/fragments/list_all.html:59
msgid "Try adding one"
msgstr ""
@@ -2604,7 +2630,7 @@ msgstr ""
#: templates/exchange_rates/fragments/table.html:56
#: templates/exchange_rates_services/fragments/table.html:57
#: templates/transactions/fragments/list_all.html:43
#: templates/transactions/fragments/list_all.html:70
msgid "Page navigation"
msgstr ""
@@ -2624,15 +2650,22 @@ msgstr ""
msgid "Last fetch"
msgstr ""
#: templates/exchange_rates_services/fragments/list.html:61
#: templates/exchange_rates_services/fragments/list.html:62
#, python-format
msgid "%(counter)s consecutive failure"
msgid_plural "%(counter)s consecutive failures"
msgstr[0] ""
msgstr[1] ""
#: templates/exchange_rates_services/fragments/list.html:69
msgid "currencies"
msgstr ""
#: templates/exchange_rates_services/fragments/list.html:61
#: templates/exchange_rates_services/fragments/list.html:69
msgid "accounts"
msgstr ""
#: templates/exchange_rates_services/fragments/list.html:69
#: templates/exchange_rates_services/fragments/list.html:77
msgid "No services configured"
msgstr ""
@@ -2882,7 +2915,11 @@ msgid "Final total"
msgstr ""
#: templates/insights/fragments/category_overview/index.html:89
#: templates/monthly_overview/fragments/monthly_summary.html:165
#: templates/insights/fragments/month_by_month.html:91
#: templates/insights/fragments/month_by_month.html:186
#: templates/insights/fragments/year_by_year.html:59
#: templates/insights/fragments/year_by_year.html:140
#: templates/monthly_overview/fragments/monthly_summary.html:167
msgid "Total"
msgstr ""
@@ -2926,6 +2963,63 @@ msgstr ""
msgid "No recent transactions"
msgstr ""
#: templates/insights/fragments/month_by_month.html:86
#: templates/insights/fragments/year_by_year.html:54
msgid "Tag"
msgstr ""
#: templates/insights/fragments/month_by_month.html:94
msgid "Jan"
msgstr ""
#: templates/insights/fragments/month_by_month.html:95
msgid "Feb"
msgstr ""
#: templates/insights/fragments/month_by_month.html:96
msgid "Mar"
msgstr ""
#: templates/insights/fragments/month_by_month.html:97
msgid "Apr"
msgstr ""
#: templates/insights/fragments/month_by_month.html:98
msgid "May"
msgstr ""
#: templates/insights/fragments/month_by_month.html:99
msgid "Jun"
msgstr ""
#: templates/insights/fragments/month_by_month.html:100
msgid "Jul"
msgstr ""
#: templates/insights/fragments/month_by_month.html:101
msgid "Aug"
msgstr ""
#: templates/insights/fragments/month_by_month.html:102
msgid "Sep"
msgstr ""
#: templates/insights/fragments/month_by_month.html:103
msgid "Oct"
msgstr ""
#: templates/insights/fragments/month_by_month.html:104
msgid "Nov"
msgstr ""
#: templates/insights/fragments/month_by_month.html:105
msgid "Dec"
msgstr ""
#: templates/insights/fragments/month_by_month.html:248
msgid "No transactions for this year"
msgstr ""
#: templates/insights/fragments/sankey.html:100
msgid "From"
msgstr ""
@@ -2934,6 +3028,10 @@ msgstr ""
msgid "Percentage"
msgstr ""
#: templates/insights/fragments/year_by_year.html:202
msgid "No transactions"
msgstr ""
#: templates/insights/pages/index.html:37
msgid "Month"
msgstr ""
@@ -2983,6 +3081,14 @@ msgstr ""
msgid "Emergency Fund"
msgstr ""
#: templates/insights/pages/index.html:127
msgid "Year by Year"
msgstr ""
#: templates/insights/pages/index.html:132
msgid "Month by Month"
msgstr ""
#: templates/installment_plans/fragments/add.html:5
msgid "Add installment plan"
msgstr ""
@@ -3054,35 +3160,40 @@ msgstr ""
msgid "Item"
msgstr ""
#: templates/monthly_overview/fragments/list.html:32
#: templates/monthly_overview/fragments/list.html:15
#: templates/transactions/fragments/list_all.html:15
msgid "late"
msgstr ""
#: templates/monthly_overview/fragments/list.html:58
msgid "No transactions this month"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:6
#: templates/monthly_overview/fragments/monthly_summary.html:7
msgid "Daily Spending Allowance"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:6
#: templates/monthly_overview/fragments/monthly_summary.html:7
msgid "This is the final total divided by the remaining days in the month"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:42
#: templates/monthly_overview/fragments/monthly_summary.html:105
#: templates/monthly_overview/fragments/monthly_summary.html:168
#: templates/monthly_overview/fragments/monthly_summary.html:44
#: templates/monthly_overview/fragments/monthly_summary.html:107
#: templates/monthly_overview/fragments/monthly_summary.html:170
msgid "current"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:71
#: templates/monthly_overview/fragments/monthly_summary.html:134
#: templates/monthly_overview/fragments/monthly_summary.html:197
#: templates/monthly_overview/fragments/monthly_summary.html:73
#: templates/monthly_overview/fragments/monthly_summary.html:136
#: templates/monthly_overview/fragments/monthly_summary.html:199
msgid "projected"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:102
#: templates/monthly_overview/fragments/monthly_summary.html:104
msgid "Expenses"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:255
#: templates/monthly_overview/fragments/monthly_summary.html:257
msgid "Distribution"
msgstr ""
@@ -3090,27 +3201,27 @@ msgstr ""
msgid "Summary"
msgstr ""
#: templates/monthly_overview/pages/overview.html:96
#: templates/monthly_overview/pages/overview.html:150
#: templates/monthly_overview/pages/overview.html:99
#: templates/monthly_overview/pages/overview.html:254
#: templates/transactions/pages/transactions.html:48
#: templates/transactions/pages/transactions.html:103
#: templates/transactions/pages/transactions.html:204
msgid "Oldest first"
msgstr ""
#: templates/monthly_overview/pages/overview.html:97
#: templates/monthly_overview/pages/overview.html:159
#: templates/monthly_overview/pages/overview.html:100
#: templates/monthly_overview/pages/overview.html:263
#: templates/transactions/pages/transactions.html:49
#: templates/transactions/pages/transactions.html:112
#: templates/transactions/pages/transactions.html:213
msgid "Newest first"
msgstr ""
#: templates/monthly_overview/pages/overview.html:106
#: templates/monthly_overview/pages/overview.html:109
#: templates/transactions/pages/transactions.html:58
msgid "Filter transactions"
msgstr ""
#: templates/monthly_overview/pages/overview.html:131
#: templates/transactions/pages/transactions.html:84
#: templates/monthly_overview/pages/overview.html:235
#: templates/transactions/pages/transactions.html:185
msgid "Order by"
msgstr ""
@@ -3351,7 +3462,7 @@ msgstr ""
msgid "transactions"
msgstr ""
#: templates/transactions/fragments/list_all.html:32
#: templates/transactions/fragments/list_all.html:58
msgid "No transactions found"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-12-20 02:59+0000\n"
"POT-Creation-Date: 2026-01-10 20:50+0000\n"
"PO-Revision-Date: 2025-11-01 01:17+0000\n"
"Last-Translator: mlystopad <mlystopadt@gmail.com>\n"
"Language-Team: Ukrainian <https://translations.herculino.com/projects/"
@@ -68,24 +68,30 @@ msgstr "Новий баланс"
#: apps/transactions/forms.py:419 apps/transactions/forms.py:516
#: apps/transactions/forms.py:523 apps/transactions/forms.py:707
#: apps/transactions/forms.py:948 apps/transactions/models.py:322
#: apps/transactions/models.py:574 apps/transactions/models.py:774
#: apps/transactions/models.py:1022
#: apps/transactions/models.py:578 apps/transactions/models.py:778
#: apps/transactions/models.py:1026
#: templates/insights/fragments/category_overview/index.html:86
#: templates/insights/fragments/category_overview/index.html:542
#: templates/insights/fragments/month_by_month.html:84
#: templates/insights/fragments/year_by_year.html:52
msgid "Category"
msgstr "Категорія"
#: apps/accounts/forms.py:132 apps/dca/forms.py:95 apps/dca/forms.py:103
#: apps/export_app/forms.py:43 apps/export_app/forms.py:132
#: apps/rules/forms.py:184 apps/rules/forms.py:194 apps/rules/models.py:45
#: apps/rules/models.py:315 apps/transactions/filters.py:68
#: apps/rules/models.py:315 apps/transactions/filters.py:73
#: apps/transactions/forms.py:51 apps/transactions/forms.py:259
#: apps/transactions/forms.py:427 apps/transactions/forms.py:532
#: apps/transactions/forms.py:540 apps/transactions/forms.py:700
#: apps/transactions/forms.py:941 apps/transactions/models.py:328
#: apps/transactions/models.py:576 apps/transactions/models.py:778
#: apps/transactions/models.py:1028 templates/includes/sidebar.html:150
#: apps/transactions/models.py:580 apps/transactions/models.py:782
#: apps/transactions/models.py:1032 templates/includes/sidebar.html:150
#: templates/insights/fragments/category_overview/index.html:40
#: templates/insights/fragments/month_by_month.html:29
#: templates/insights/fragments/month_by_month.html:32
#: templates/insights/fragments/year_by_year.html:24
#: templates/insights/fragments/year_by_year.html:27
#: templates/tags/fragments/list.html:9 templates/tags/pages/index.html:4
msgid "Tags"
msgstr "Мітки"
@@ -93,7 +99,7 @@ msgstr "Мітки"
#: apps/accounts/models.py:12 apps/accounts/models.py:29 apps/dca/models.py:13
#: apps/import_app/models.py:14 apps/rules/models.py:13
#: apps/transactions/models.py:214 apps/transactions/models.py:239
#: apps/transactions/models.py:263 apps/transactions/models.py:990
#: apps/transactions/models.py:263 apps/transactions/models.py:994
#: templates/account_groups/fragments/list.html:22
#: templates/accounts/fragments/list.html:22
#: templates/categories/fragments/table.html:17
@@ -166,8 +172,8 @@ msgstr ""
#: apps/transactions/forms.py:63 apps/transactions/forms.py:271
#: apps/transactions/forms.py:386 apps/transactions/forms.py:692
#: apps/transactions/forms.py:933 apps/transactions/models.py:294
#: apps/transactions/models.py:534 apps/transactions/models.py:756
#: apps/transactions/models.py:996
#: apps/transactions/models.py:538 apps/transactions/models.py:760
#: apps/transactions/models.py:1000
#: templates/installment_plans/fragments/table.html:17
#: templates/quick_transactions/fragments/list.html:14
#: templates/recurring_transactions/fragments/table.html:19
@@ -176,11 +182,11 @@ msgid "Account"
msgstr "Рахунок"
#: apps/accounts/models.py:76 apps/export_app/forms.py:19
#: apps/export_app/forms.py:129 apps/transactions/filters.py:52
#: apps/export_app/forms.py:129 apps/transactions/filters.py:57
#: templates/accounts/fragments/list.html:9
#: templates/accounts/pages/index.html:4 templates/includes/sidebar.html:162
#: templates/includes/sidebar.html:164
#: templates/monthly_overview/pages/overview.html:75
#: templates/monthly_overview/pages/overview.html:77
#: templates/transactions/pages/transactions.html:26
msgid "Accounts"
msgstr "Рахунки"
@@ -195,8 +201,8 @@ msgstr "Групу рахунків успішно додано"
#: apps/accounts/views/account_groups.py:69
#: apps/accounts/views/account_groups.py:152 apps/accounts/views/accounts.py:68
#: apps/accounts/views/accounts.py:106 apps/dca/views.py:63
#: apps/dca/views.py:146 apps/rules/views.py:118 apps/rules/views.py:228
#: apps/accounts/views/accounts.py:106 apps/dca/views.py:62
#: apps/dca/views.py:145 apps/rules/views.py:118 apps/rules/views.py:228
#: apps/transactions/views/categories.py:91
#: apps/transactions/views/categories.py:129
#: apps/transactions/views/entities.py:91
@@ -210,7 +216,7 @@ msgid "Account Group updated successfully"
msgstr "Групу рахунків успішно оновлено"
#: apps/accounts/views/account_groups.py:111
#: apps/accounts/views/accounts.py:145 apps/dca/views.py:105
#: apps/accounts/views/accounts.py:145 apps/dca/views.py:104
#: apps/rules/views.py:185 apps/transactions/views/categories.py:168
#: apps/transactions/views/entities.py:130 apps/transactions/views/tags.py:130
msgid "Item no longer shared with you"
@@ -221,14 +227,14 @@ msgid "Account Group deleted successfully"
msgstr "Групу рахунків успішно видалено"
#: apps/accounts/views/account_groups.py:135
#: apps/accounts/views/accounts.py:189 apps/dca/views.py:129
#: apps/accounts/views/accounts.py:189 apps/dca/views.py:128
#: apps/rules/views.py:210 apps/transactions/views/categories.py:192
#: apps/transactions/views/entities.py:154 apps/transactions/views/tags.py:154
msgid "Ownership taken successfully"
msgstr "Успішно прийнято право власності"
#: apps/accounts/views/account_groups.py:165
#: apps/accounts/views/accounts.py:119 apps/dca/views.py:159
#: apps/accounts/views/accounts.py:119 apps/dca/views.py:158
#: apps/rules/views.py:241 apps/transactions/views/categories.py:142
#: apps/transactions/views/entities.py:184 apps/transactions/views/tags.py:184
msgid "Configuration saved successfully"
@@ -368,7 +374,7 @@ msgid "Public"
msgstr "Public"
#: apps/common/templatetags/natural.py:20
#: templates/monthly_overview/fragments/monthly_summary.html:9
#: templates/monthly_overview/fragments/monthly_summary.html:10
msgid "today"
msgstr "сьогодні"
@@ -472,10 +478,10 @@ msgstr "Видалити"
#: apps/common/widgets/tom_select.py:15
#: templates/mini_tools/unit_price_calculator.html:180
#: templates/monthly_overview/pages/overview.html:171
#: templates/monthly_overview/pages/overview.html:183
#: templates/transactions/pages/transactions.html:124
#: templates/transactions/pages/transactions.html:136
#: templates/monthly_overview/pages/overview.html:275
#: templates/monthly_overview/pages/overview.html:287
#: templates/transactions/pages/transactions.html:225
#: templates/transactions/pages/transactions.html:237
msgid "Clear"
msgstr "Чисто"
@@ -514,11 +520,11 @@ msgid "Decimal Places"
msgstr "Десяткові знаки"
#: apps/currencies/models.py:45 apps/export_app/forms.py:25
#: apps/export_app/forms.py:130 apps/transactions/filters.py:59
#: apps/export_app/forms.py:130 apps/transactions/filters.py:64
#: templates/currencies/fragments/list.html:9
#: templates/currencies/pages/index.html:4 templates/includes/sidebar.html:176
#: templates/includes/sidebar.html:178
#: templates/monthly_overview/pages/overview.html:62
#: templates/monthly_overview/pages/overview.html:63
#: templates/transactions/pages/transactions.html:12
msgid "Currencies"
msgstr "Валюти"
@@ -579,9 +585,9 @@ msgstr "Назва сервісу"
msgid "Service Type"
msgstr "Тип сервісу"
#: apps/currencies/models.py:118 apps/transactions/models.py:218
#: apps/transactions/models.py:242 apps/transactions/models.py:266
#: templates/categories/fragments/list.html:16
#: apps/currencies/models.py:118 apps/transactions/filters.py:27
#: apps/transactions/models.py:218 apps/transactions/models.py:242
#: apps/transactions/models.py:266 templates/categories/fragments/list.html:16
#: templates/entities/fragments/list.html:16
#: templates/installment_plans/fragments/list.html:16
#: templates/recurring_transactions/fragments/list.html:16
@@ -609,11 +615,11 @@ msgstr "Інтервал"
msgid "Last Successful Fetch"
msgstr "Остання успішна вибірка"
#: apps/currencies/models.py:141
#: apps/currencies/models.py:143
msgid "Target Currencies"
msgstr "Цільові валюти"
#: apps/currencies/models.py:143
#: apps/currencies/models.py:145
msgid ""
"Select currencies to fetch exchange rates for. Rates will be fetched for "
"each currency against their set exchange currency."
@@ -621,11 +627,11 @@ msgstr ""
"Оберіть валюти для завантаження курсів обміну. Курси будуть завантажені для "
"кожної валюти відносно встановленої валюти обміну."
#: apps/currencies/models.py:151
#: apps/currencies/models.py:153
msgid "Target Accounts"
msgstr "Цільові Рахунки"
#: apps/currencies/models.py:153
#: apps/currencies/models.py:155
msgid ""
"Select accounts to fetch exchange rates for. Rates will be fetched for each "
"account's currency against their set exchange currency."
@@ -633,35 +639,35 @@ msgstr ""
"Оберіть рахунки для завантаження курсів обміну. Курси будуть завантажені для "
"валюти кожного рахунку відносно встановленої валюти обміну."
#: apps/currencies/models.py:160
#: apps/currencies/models.py:162
#, fuzzy
#| msgid "Exchange Rate"
msgid "Single exchange rate"
msgstr "Обмінний курс"
#: apps/currencies/models.py:163
#: apps/currencies/models.py:165
msgid "Create one exchange rate and keep updating it. Avoids database clutter."
msgstr ""
"Створіть один курс обміну та оновлюйте його постійно. Це запобігає "
"засміченню бази даних."
#: apps/currencies/models.py:168
#: apps/currencies/models.py:170
msgid "Exchange Rate Service"
msgstr "Сервіс Курсів Обміну"
#: apps/currencies/models.py:169
#: apps/currencies/models.py:171
msgid "Exchange Rate Services"
msgstr "Сервіси Курсів Обміну"
#: apps/currencies/models.py:221
#: apps/currencies/models.py:223
msgid "'Every X hours' interval type requires a positive integer."
msgstr "Інтервал типу «Кожні X годин» потребує додатнього цілого числа."
#: apps/currencies/models.py:230
#: apps/currencies/models.py:232
msgid "'Every X hours' interval must be between 1 and 24."
msgstr "Інтервал типу «Кожні X годин» повинен бути між 1 та 24."
#: apps/currencies/models.py:244
#: apps/currencies/models.py:246
msgid ""
"Invalid hour format. Use comma-separated hours (0-23) and/or ranges (e.g., "
"'1-5,8,10-12')."
@@ -669,7 +675,7 @@ msgstr ""
"Неправильний формат годин. Використовуйте години, розділені комами (023) та/"
"або діапазони (наприклад, '1-5,8,10-12')."
#: apps/currencies/models.py:255
#: apps/currencies/models.py:257
msgid ""
"Invalid format. Please check the requirements for your selected interval "
"type."
@@ -770,8 +776,8 @@ msgstr "Валюта платежу"
#: apps/dca/models.py:26 apps/dca/models.py:181 apps/rules/forms.py:180
#: apps/rules/forms.py:196 apps/rules/models.py:43 apps/rules/models.py:295
#: apps/transactions/forms.py:413 apps/transactions/forms.py:560
#: apps/transactions/models.py:318 apps/transactions/models.py:583
#: apps/transactions/models.py:784 apps/transactions/models.py:1018
#: apps/transactions/models.py:318 apps/transactions/models.py:587
#: apps/transactions/models.py:788 apps/transactions/models.py:1022
msgid "Notes"
msgstr "Примітки"
@@ -803,27 +809,27 @@ msgstr ""
msgid "DCA Entries"
msgstr ""
#: apps/dca/views.py:39
#: apps/dca/views.py:38
msgid "DCA Strategy added successfully"
msgstr ""
#: apps/dca/views.py:76
#: apps/dca/views.py:75
msgid "DCA Strategy updated successfully"
msgstr ""
#: apps/dca/views.py:108
#: apps/dca/views.py:107
msgid "DCA strategy deleted successfully"
msgstr ""
#: apps/dca/views.py:238
#: apps/dca/views.py:237
msgid "Entry added successfully"
msgstr "Запис успішно додано"
#: apps/dca/views.py:265
#: apps/dca/views.py:264
msgid "Entry updated successfully"
msgstr "Запис успішно оновлено"
#: apps/dca/views.py:291
#: apps/dca/views.py:290
msgid "Entry deleted successfully"
msgstr "Запис успішно видалено"
@@ -843,34 +849,42 @@ msgid "Transactions"
msgstr "Транзакції"
#: apps/export_app/forms.py:37 apps/export_app/forms.py:131
#: apps/transactions/filters.py:63 templates/categories/fragments/list.html:9
#: apps/transactions/filters.py:68 templates/categories/fragments/list.html:9
#: templates/categories/pages/index.html:4 templates/includes/sidebar.html:144
#: templates/insights/fragments/month_by_month.html:18
#: templates/insights/fragments/month_by_month.html:21
#: templates/insights/fragments/year_by_year.html:13
#: templates/insights/fragments/year_by_year.html:16
msgid "Categories"
msgstr "Категорії"
#: apps/export_app/forms.py:49 apps/export_app/forms.py:133
#: apps/rules/forms.py:185 apps/rules/forms.py:195 apps/rules/models.py:46
#: apps/rules/models.py:307 apps/transactions/filters.py:73
#: apps/rules/models.py:307 apps/transactions/filters.py:78
#: apps/transactions/forms.py:59 apps/transactions/forms.py:267
#: apps/transactions/forms.py:435 apps/transactions/forms.py:715
#: apps/transactions/forms.py:956 apps/transactions/models.py:277
#: apps/transactions/models.py:333 apps/transactions/models.py:579
#: apps/transactions/models.py:781 apps/transactions/models.py:1033
#: apps/transactions/models.py:333 apps/transactions/models.py:583
#: apps/transactions/models.py:785 apps/transactions/models.py:1037
#: templates/entities/fragments/list.html:9
#: templates/entities/pages/index.html:4 templates/includes/sidebar.html:156
#: templates/insights/fragments/category_overview/index.html:54
#: templates/insights/fragments/month_by_month.html:40
#: templates/insights/fragments/month_by_month.html:43
#: templates/insights/fragments/year_by_year.html:35
#: templates/insights/fragments/year_by_year.html:38
msgid "Entities"
msgstr ""
#: apps/export_app/forms.py:55 apps/export_app/forms.py:137
#: apps/transactions/models.py:821 templates/includes/sidebar.html:110
#: apps/transactions/models.py:825 templates/includes/sidebar.html:110
#: templates/recurring_transactions/fragments/list.html:9
#: templates/recurring_transactions/pages/index.html:4
msgid "Recurring Transactions"
msgstr "Регулярні транзакції"
#: apps/export_app/forms.py:61 apps/export_app/forms.py:135
#: apps/transactions/models.py:597 templates/includes/sidebar.html:104
#: apps/transactions/models.py:601 templates/includes/sidebar.html:104
#: templates/installment_plans/fragments/list.html:9
#: templates/installment_plans/pages/index.html:4
msgid "Installment Plans"
@@ -1024,10 +1038,12 @@ msgid "Run deleted successfully"
msgstr ""
#: apps/insights/forms.py:118 apps/insights/utils/sankey.py:36
#: apps/insights/utils/sankey.py:167 apps/transactions/filters.py:186
#: apps/insights/utils/sankey.py:167 apps/transactions/filters.py:203
#: templates/insights/fragments/category_overview/index.html:96
#: templates/insights/fragments/category_overview/index.html:407
#: templates/insights/fragments/category_overview/index.html:436
#: templates/insights/fragments/month_by_month.html:119
#: templates/insights/fragments/year_by_year.html:73
msgid "Uncategorized"
msgstr ""
@@ -1120,15 +1136,15 @@ msgstr ""
#: apps/rules/forms.py:174 apps/rules/forms.py:188 apps/rules/models.py:36
#: apps/rules/models.py:271 apps/transactions/forms.py:377
#: apps/transactions/models.py:301 apps/transactions/models.py:539
#: apps/transactions/models.py:762 apps/transactions/models.py:1003
#: apps/transactions/models.py:301 apps/transactions/models.py:543
#: apps/transactions/models.py:766 apps/transactions/models.py:1007
msgid "Type"
msgstr ""
#: apps/rules/forms.py:175 apps/rules/forms.py:189 apps/rules/models.py:37
#: apps/rules/models.py:275 apps/transactions/filters.py:22
#: apps/transactions/forms.py:381 apps/transactions/models.py:303
#: apps/transactions/models.py:1005 templates/cotton/transaction/item.html:20
#: apps/transactions/models.py:1009 templates/cotton/transaction/item.html:20
#: templates/cotton/transaction/item.html:31
#: templates/transactions/widgets/paid_toggle_button.html:10
#: templates/transactions/widgets/unselectable_paid_toggle_button.html:13
@@ -1139,14 +1155,14 @@ msgstr ""
#: apps/rules/models.py:283 apps/transactions/forms.py:71
#: apps/transactions/forms.py:397 apps/transactions/forms.py:547
#: apps/transactions/forms.py:721 apps/transactions/models.py:305
#: apps/transactions/models.py:557 apps/transactions/models.py:786
#: apps/transactions/models.py:561 apps/transactions/models.py:790
msgid "Reference Date"
msgstr ""
#: apps/rules/forms.py:178 apps/rules/forms.py:192 apps/rules/models.py:41
#: apps/rules/models.py:287 apps/transactions/forms.py:404
#: apps/transactions/models.py:311 apps/transactions/models.py:767
#: apps/transactions/models.py:1011
#: apps/transactions/models.py:311 apps/transactions/models.py:771
#: apps/transactions/models.py:1015
#: templates/insights/fragments/sankey.html:102
#: templates/installment_plans/fragments/table.html:18
#: templates/quick_transactions/fragments/list.html:15
@@ -1157,27 +1173,27 @@ msgstr ""
#: apps/rules/forms.py:179 apps/rules/forms.py:193 apps/rules/models.py:14
#: apps/rules/models.py:42 apps/rules/models.py:291
#: apps/transactions/forms.py:408 apps/transactions/forms.py:551
#: apps/transactions/models.py:316 apps/transactions/models.py:541
#: apps/transactions/models.py:770 apps/transactions/models.py:1016
#: apps/transactions/models.py:316 apps/transactions/models.py:545
#: apps/transactions/models.py:774 apps/transactions/models.py:1020
msgid "Description"
msgstr ""
#: apps/rules/forms.py:182 apps/rules/forms.py:198 apps/rules/models.py:47
#: apps/rules/models.py:299 apps/transactions/models.py:355
#: apps/transactions/models.py:1038
#: apps/transactions/models.py:1042
msgid "Internal Note"
msgstr ""
#: apps/rules/forms.py:183 apps/rules/forms.py:199 apps/rules/models.py:48
#: apps/rules/models.py:303 apps/transactions/models.py:357
#: apps/transactions/models.py:1040
#: apps/transactions/models.py:1044
msgid "Internal ID"
msgstr ""
#: apps/rules/forms.py:186 apps/rules/forms.py:200 apps/rules/models.py:40
#: apps/rules/models.py:319 apps/transactions/forms.py:564
#: apps/transactions/models.py:215 apps/transactions/models.py:306
#: apps/transactions/models.py:1006
#: apps/transactions/models.py:1010
msgid "Mute"
msgstr ""
@@ -1333,55 +1349,67 @@ msgstr ""
msgid "Projected"
msgstr ""
#: apps/transactions/filters.py:40
#: apps/transactions/filters.py:28 templates/categories/fragments/table.html:18
msgid "Muted"
msgstr ""
#: apps/transactions/filters.py:45
msgid "Content"
msgstr ""
#: apps/transactions/filters.py:46
#: apps/transactions/filters.py:51
msgid "Transaction Type"
msgstr ""
#: apps/transactions/filters.py:84
msgid "Date from"
msgstr ""
#: apps/transactions/filters.py:89 apps/transactions/filters.py:99
msgid "Until"
#: apps/transactions/filters.py:89
msgid "Mute Status"
msgstr ""
#: apps/transactions/filters.py:94
msgid "Reference date from"
msgid "Date from"
msgstr ""
#: apps/transactions/filters.py:99 apps/transactions/filters.py:109
msgid "Until"
msgstr ""
#: apps/transactions/filters.py:104
msgid "Reference date from"
msgstr ""
#: apps/transactions/filters.py:114
msgid "Amount min"
msgstr ""
#: apps/transactions/filters.py:109
#: apps/transactions/filters.py:119
msgid "Amount max"
msgstr ""
#: apps/transactions/filters.py:185
#: apps/transactions/filters.py:202
#, fuzzy
#| msgid "Category"
msgid "Categorized"
msgstr "Категорія"
#: apps/transactions/filters.py:192
#: apps/transactions/filters.py:209
msgid "Tagged"
msgstr ""
#: apps/transactions/filters.py:192
#: apps/transactions/filters.py:209
#: templates/insights/fragments/category_overview/index.html:189
#: templates/insights/fragments/month_by_month.html:121
#: templates/insights/fragments/year_by_year.html:75
msgid "Untagged"
msgstr ""
#: apps/transactions/filters.py:198
#: apps/transactions/filters.py:215
msgid "Any entity"
msgstr ""
#: apps/transactions/filters.py:199
#: apps/transactions/filters.py:216
#: templates/insights/fragments/category_overview/index.html:282
#: templates/insights/fragments/month_by_month.html:123
#: templates/insights/fragments/year_by_year.html:77
msgid "No entity"
msgstr ""
@@ -1469,10 +1497,12 @@ msgid ""
msgstr ""
#: apps/transactions/models.py:276
#: templates/insights/fragments/month_by_month.html:88
#: templates/insights/fragments/year_by_year.html:56
msgid "Entity"
msgstr ""
#: apps/transactions/models.py:288 apps/transactions/models.py:983
#: apps/transactions/models.py:288 apps/transactions/models.py:987
#: templates/calendar_view/fragments/list.html:42
#: templates/calendar_view/fragments/list.html:44
#: templates/calendar_view/fragments/list.html:52
@@ -1480,11 +1510,11 @@ msgstr ""
#: templates/cotton/ui/quick_transactions_buttons.html:10
#: templates/cotton/ui/transactions_fab.html:10
#: templates/insights/fragments/category_overview/index.html:87
#: templates/monthly_overview/fragments/monthly_summary.html:39
#: templates/monthly_overview/fragments/monthly_summary.html:41
msgid "Income"
msgstr ""
#: apps/transactions/models.py:289 apps/transactions/models.py:984
#: apps/transactions/models.py:289 apps/transactions/models.py:988
#: templates/calendar_view/fragments/list.html:46
#: templates/calendar_view/fragments/list.html:48
#: templates/calendar_view/fragments/list.html:56
@@ -1495,11 +1525,11 @@ msgstr ""
msgid "Expense"
msgstr ""
#: apps/transactions/models.py:344 apps/transactions/models.py:596
#: apps/transactions/models.py:344 apps/transactions/models.py:600
msgid "Installment Plan"
msgstr ""
#: apps/transactions/models.py:353 apps/transactions/models.py:820
#: apps/transactions/models.py:353 apps/transactions/models.py:824
msgid "Recurring Transaction"
msgstr ""
@@ -1511,113 +1541,113 @@ msgstr ""
msgid "Deleted At"
msgstr ""
#: apps/transactions/models.py:476 templates/tags/fragments/table.html:69
#: apps/transactions/models.py:480 templates/tags/fragments/table.html:69
msgid "No tags"
msgstr ""
#: apps/transactions/models.py:478
#: apps/transactions/models.py:482
msgid "No category"
msgstr ""
#: apps/transactions/models.py:480
#: apps/transactions/models.py:484
msgid "No description"
msgstr ""
#: apps/transactions/models.py:528 templates/includes/sidebar.html:57
#: apps/transactions/models.py:532 templates/includes/sidebar.html:57
msgid "Yearly"
msgstr ""
#: apps/transactions/models.py:529 apps/users/models.py:464
#: apps/transactions/models.py:533 apps/users/models.py:464
#: templates/includes/sidebar.html:51
msgid "Monthly"
msgstr ""
#: apps/transactions/models.py:530
#: apps/transactions/models.py:534
msgid "Weekly"
msgstr ""
#: apps/transactions/models.py:531
#: apps/transactions/models.py:535
msgid "Daily"
msgstr ""
#: apps/transactions/models.py:544
#: apps/transactions/models.py:548
msgid "Number of Installments"
msgstr ""
#: apps/transactions/models.py:549
#: apps/transactions/models.py:553
msgid "Installment Start"
msgstr ""
#: apps/transactions/models.py:550
#: apps/transactions/models.py:554
msgid "The installment number to start counting from"
msgstr ""
#: apps/transactions/models.py:555 apps/transactions/models.py:790
#: apps/transactions/models.py:559 apps/transactions/models.py:794
msgid "Start Date"
msgstr ""
#: apps/transactions/models.py:559 apps/transactions/models.py:791
#: apps/transactions/models.py:563 apps/transactions/models.py:795
msgid "End Date"
msgstr ""
#: apps/transactions/models.py:564
#: apps/transactions/models.py:568
msgid "Recurrence"
msgstr ""
#: apps/transactions/models.py:567
#: apps/transactions/models.py:571
msgid "Installment Amount"
msgstr ""
#: apps/transactions/models.py:586 apps/transactions/models.py:810
#: apps/transactions/models.py:590 apps/transactions/models.py:814
msgid "Add description to transactions"
msgstr ""
#: apps/transactions/models.py:589 apps/transactions/models.py:813
#: apps/transactions/models.py:593 apps/transactions/models.py:817
msgid "Add notes to transactions"
msgstr ""
#: apps/transactions/models.py:749
#: apps/transactions/models.py:753
msgid "day(s)"
msgstr ""
#: apps/transactions/models.py:750
#: apps/transactions/models.py:754
msgid "week(s)"
msgstr ""
#: apps/transactions/models.py:751
#: apps/transactions/models.py:755
msgid "month(s)"
msgstr ""
#: apps/transactions/models.py:752
#: apps/transactions/models.py:756
msgid "year(s)"
msgstr ""
#: apps/transactions/models.py:754
#: apps/transactions/models.py:758
#: templates/recurring_transactions/fragments/list.html:18
msgid "Paused"
msgstr ""
#: apps/transactions/models.py:793
#: apps/transactions/models.py:797
msgid "Recurrence Type"
msgstr ""
#: apps/transactions/models.py:796
#: apps/transactions/models.py:800
msgid "Recurrence Interval"
msgstr ""
#: apps/transactions/models.py:799
#: apps/transactions/models.py:803
msgid "Keep at most"
msgstr ""
#: apps/transactions/models.py:803
#: apps/transactions/models.py:807
msgid "Last Generated Date"
msgstr ""
#: apps/transactions/models.py:806
#: apps/transactions/models.py:810
msgid "Last Generated Reference Date"
msgstr ""
#: apps/transactions/models.py:1050
#: apps/transactions/models.py:1054
#: apps/transactions/views/quick_transactions.py:178
#: apps/transactions/views/quick_transactions.py:187
#: apps/transactions/views/quick_transactions.py:189
@@ -1626,7 +1656,7 @@ msgstr ""
msgid "Quick Transaction"
msgstr ""
#: apps/transactions/models.py:1051 templates/includes/sidebar.html:98
#: apps/transactions/models.py:1055 templates/includes/sidebar.html:98
#: templates/quick_transactions/pages/index.html:5
#: templates/quick_transactions/pages/index.html:15
msgid "Quick Transactions"
@@ -1734,7 +1764,7 @@ msgstr "Рахунок успішно видалено"
#: apps/transactions/views/quick_transactions.py:156
#: apps/transactions/views/transactions.py:53
#: apps/transactions/views/transactions.py:228
#: apps/transactions/views/transactions.py:238
msgid "Transaction added successfully"
msgstr ""
@@ -1774,30 +1804,30 @@ msgstr ""
msgid "Tag deleted successfully"
msgstr ""
#: apps/transactions/views/transactions.py:252
#: apps/transactions/views/transactions.py:262
msgid "Transaction updated successfully"
msgstr ""
#: apps/transactions/views/transactions.py:303
#: apps/transactions/views/transactions.py:313
#, python-format
msgid "%(count)s transaction updated successfully"
msgid_plural "%(count)s transactions updated successfully"
msgstr[0] ""
msgstr[1] ""
#: apps/transactions/views/transactions.py:339
#: apps/transactions/views/transactions.py:349
msgid "Transaction duplicated successfully"
msgstr ""
#: apps/transactions/views/transactions.py:381
#: apps/transactions/views/transactions.py:391
msgid "Transaction deleted successfully"
msgstr ""
#: apps/transactions/views/transactions.py:399
#: apps/transactions/views/transactions.py:409
msgid "Transaction restored successfully"
msgstr ""
#: apps/transactions/views/transactions.py:425
#: apps/transactions/views/transactions.py:435
msgid "Transfer added successfully"
msgstr ""
@@ -1839,10 +1869,10 @@ msgid "This account is deactivated"
msgstr ""
#: apps/users/forms.py:62 apps/users/forms.py:75 apps/users/forms.py:97
#: templates/monthly_overview/pages/overview.html:95
#: templates/monthly_overview/pages/overview.html:141
#: templates/monthly_overview/pages/overview.html:98
#: templates/monthly_overview/pages/overview.html:245
#: templates/transactions/pages/transactions.html:47
#: templates/transactions/pages/transactions.html:94
#: templates/transactions/pages/transactions.html:195
msgid "Default"
msgstr ""
@@ -2262,10 +2292,6 @@ msgstr ""
msgid "Edit category"
msgstr ""
#: templates/categories/fragments/table.html:18
msgid "Muted"
msgstr ""
#: templates/categories/fragments/table.html:73
#: templates/insights/fragments/category_overview/index.html:552
msgid "No categories"
@@ -2284,9 +2310,9 @@ msgstr ""
#: templates/cotton/config/search.html:6
#: templates/import_app/fragments/profiles/list_presets.html:13
#: templates/monthly_overview/pages/overview.html:115
#: templates/monthly_overview/pages/overview.html:219
#: templates/rules/fragments/transaction_rule/dry_run/visual.html:57
#: templates/transactions/pages/transactions.html:67
#: templates/transactions/pages/transactions.html:168
msgid "Search"
msgstr ""
@@ -2514,8 +2540,8 @@ msgid "No entries for this DCA"
msgstr ""
#: templates/dca/fragments/strategy/details.html:120
#: templates/monthly_overview/fragments/list.html:33
#: templates/transactions/fragments/list_all.html:33
#: templates/monthly_overview/fragments/list.html:59
#: templates/transactions/fragments/list_all.html:59
msgid "Try adding one"
msgstr ""
@@ -2640,7 +2666,7 @@ msgstr ""
#: templates/exchange_rates/fragments/table.html:56
#: templates/exchange_rates_services/fragments/table.html:57
#: templates/transactions/fragments/list_all.html:43
#: templates/transactions/fragments/list_all.html:70
msgid "Page navigation"
msgstr ""
@@ -2660,15 +2686,23 @@ msgstr ""
msgid "Last fetch"
msgstr ""
#: templates/exchange_rates_services/fragments/list.html:61
#: templates/exchange_rates_services/fragments/list.html:62
#, python-format
msgid "%(counter)s consecutive failure"
msgid_plural "%(counter)s consecutive failures"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
#: templates/exchange_rates_services/fragments/list.html:69
msgid "currencies"
msgstr ""
#: templates/exchange_rates_services/fragments/list.html:61
#: templates/exchange_rates_services/fragments/list.html:69
msgid "accounts"
msgstr ""
#: templates/exchange_rates_services/fragments/list.html:69
#: templates/exchange_rates_services/fragments/list.html:77
msgid "No services configured"
msgstr ""
@@ -2918,7 +2952,11 @@ msgid "Final total"
msgstr ""
#: templates/insights/fragments/category_overview/index.html:89
#: templates/monthly_overview/fragments/monthly_summary.html:165
#: templates/insights/fragments/month_by_month.html:91
#: templates/insights/fragments/month_by_month.html:186
#: templates/insights/fragments/year_by_year.html:59
#: templates/insights/fragments/year_by_year.html:140
#: templates/monthly_overview/fragments/monthly_summary.html:167
msgid "Total"
msgstr ""
@@ -2962,6 +3000,69 @@ msgstr ""
msgid "No recent transactions"
msgstr ""
#: templates/insights/fragments/month_by_month.html:86
#: templates/insights/fragments/year_by_year.html:54
#, fuzzy
#| msgid "Tags"
msgid "Tag"
msgstr "Мітки"
#: templates/insights/fragments/month_by_month.html:94
msgid "Jan"
msgstr ""
#: templates/insights/fragments/month_by_month.html:95
msgid "Feb"
msgstr ""
#: templates/insights/fragments/month_by_month.html:96
msgid "Mar"
msgstr ""
#: templates/insights/fragments/month_by_month.html:97
msgid "Apr"
msgstr ""
#: templates/insights/fragments/month_by_month.html:98
msgid "May"
msgstr ""
#: templates/insights/fragments/month_by_month.html:99
msgid "Jun"
msgstr ""
#: templates/insights/fragments/month_by_month.html:100
msgid "Jul"
msgstr ""
#: templates/insights/fragments/month_by_month.html:101
msgid "Aug"
msgstr ""
#: templates/insights/fragments/month_by_month.html:102
msgid "Sep"
msgstr ""
#: templates/insights/fragments/month_by_month.html:103
msgid "Oct"
msgstr ""
#: templates/insights/fragments/month_by_month.html:104
#, fuzzy
#| msgid "Now"
msgid "Nov"
msgstr "Зараз"
#: templates/insights/fragments/month_by_month.html:105
msgid "Dec"
msgstr ""
#: templates/insights/fragments/month_by_month.html:248
#, fuzzy
#| msgid "Transactions"
msgid "No transactions for this year"
msgstr "Транзакції"
#: templates/insights/fragments/sankey.html:100
msgid "From"
msgstr ""
@@ -2970,6 +3071,12 @@ msgstr ""
msgid "Percentage"
msgstr ""
#: templates/insights/fragments/year_by_year.html:202
#, fuzzy
#| msgid "Transactions"
msgid "No transactions"
msgstr "Транзакції"
#: templates/insights/pages/index.html:37
msgid "Month"
msgstr ""
@@ -3019,6 +3126,14 @@ msgstr ""
msgid "Emergency Fund"
msgstr ""
#: templates/insights/pages/index.html:127
msgid "Year by Year"
msgstr ""
#: templates/insights/pages/index.html:132
msgid "Month by Month"
msgstr ""
#: templates/installment_plans/fragments/add.html:5
msgid "Add installment plan"
msgstr ""
@@ -3090,35 +3205,40 @@ msgstr ""
msgid "Item"
msgstr ""
#: templates/monthly_overview/fragments/list.html:32
#: templates/monthly_overview/fragments/list.html:15
#: templates/transactions/fragments/list_all.html:15
msgid "late"
msgstr ""
#: templates/monthly_overview/fragments/list.html:58
msgid "No transactions this month"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:6
#: templates/monthly_overview/fragments/monthly_summary.html:7
msgid "Daily Spending Allowance"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:6
#: templates/monthly_overview/fragments/monthly_summary.html:7
msgid "This is the final total divided by the remaining days in the month"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:42
#: templates/monthly_overview/fragments/monthly_summary.html:105
#: templates/monthly_overview/fragments/monthly_summary.html:168
#: templates/monthly_overview/fragments/monthly_summary.html:44
#: templates/monthly_overview/fragments/monthly_summary.html:107
#: templates/monthly_overview/fragments/monthly_summary.html:170
msgid "current"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:71
#: templates/monthly_overview/fragments/monthly_summary.html:134
#: templates/monthly_overview/fragments/monthly_summary.html:197
#: templates/monthly_overview/fragments/monthly_summary.html:73
#: templates/monthly_overview/fragments/monthly_summary.html:136
#: templates/monthly_overview/fragments/monthly_summary.html:199
msgid "projected"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:102
#: templates/monthly_overview/fragments/monthly_summary.html:104
msgid "Expenses"
msgstr ""
#: templates/monthly_overview/fragments/monthly_summary.html:255
#: templates/monthly_overview/fragments/monthly_summary.html:257
msgid "Distribution"
msgstr ""
@@ -3126,27 +3246,27 @@ msgstr ""
msgid "Summary"
msgstr ""
#: templates/monthly_overview/pages/overview.html:96
#: templates/monthly_overview/pages/overview.html:150
#: templates/monthly_overview/pages/overview.html:99
#: templates/monthly_overview/pages/overview.html:254
#: templates/transactions/pages/transactions.html:48
#: templates/transactions/pages/transactions.html:103
#: templates/transactions/pages/transactions.html:204
msgid "Oldest first"
msgstr ""
#: templates/monthly_overview/pages/overview.html:97
#: templates/monthly_overview/pages/overview.html:159
#: templates/monthly_overview/pages/overview.html:100
#: templates/monthly_overview/pages/overview.html:263
#: templates/transactions/pages/transactions.html:49
#: templates/transactions/pages/transactions.html:112
#: templates/transactions/pages/transactions.html:213
msgid "Newest first"
msgstr ""
#: templates/monthly_overview/pages/overview.html:106
#: templates/monthly_overview/pages/overview.html:109
#: templates/transactions/pages/transactions.html:58
msgid "Filter transactions"
msgstr ""
#: templates/monthly_overview/pages/overview.html:131
#: templates/transactions/pages/transactions.html:84
#: templates/monthly_overview/pages/overview.html:235
#: templates/transactions/pages/transactions.html:185
msgid "Order by"
msgstr ""
@@ -3387,7 +3507,7 @@ msgstr ""
msgid "transactions"
msgstr ""
#: templates/transactions/fragments/list_all.html:32
#: templates/transactions/fragments/list_all.html:58
msgid "No transactions found"
msgstr ""

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-12-20 02:59+0000\n"
"POT-Creation-Date: 2026-01-10 20:50+0000\n"
"PO-Revision-Date: 2025-10-08 16:17+0000\n"
"Last-Translator: doody <doodykimo@gmail.com>\n"
"Language-Team: Chinese (Traditional Han script) <https://translations."
@@ -67,24 +67,30 @@ msgstr "新的餘額"
#: apps/transactions/forms.py:419 apps/transactions/forms.py:516
#: apps/transactions/forms.py:523 apps/transactions/forms.py:707
#: apps/transactions/forms.py:948 apps/transactions/models.py:322
#: apps/transactions/models.py:574 apps/transactions/models.py:774
#: apps/transactions/models.py:1022
#: apps/transactions/models.py:578 apps/transactions/models.py:778
#: apps/transactions/models.py:1026
#: templates/insights/fragments/category_overview/index.html:86
#: templates/insights/fragments/category_overview/index.html:542
#: templates/insights/fragments/month_by_month.html:84
#: templates/insights/fragments/year_by_year.html:52
msgid "Category"
msgstr "分類"
#: apps/accounts/forms.py:132 apps/dca/forms.py:95 apps/dca/forms.py:103
#: apps/export_app/forms.py:43 apps/export_app/forms.py:132
#: apps/rules/forms.py:184 apps/rules/forms.py:194 apps/rules/models.py:45
#: apps/rules/models.py:315 apps/transactions/filters.py:68
#: apps/rules/models.py:315 apps/transactions/filters.py:73
#: apps/transactions/forms.py:51 apps/transactions/forms.py:259
#: apps/transactions/forms.py:427 apps/transactions/forms.py:532
#: apps/transactions/forms.py:540 apps/transactions/forms.py:700
#: apps/transactions/forms.py:941 apps/transactions/models.py:328
#: apps/transactions/models.py:576 apps/transactions/models.py:778
#: apps/transactions/models.py:1028 templates/includes/sidebar.html:150
#: apps/transactions/models.py:580 apps/transactions/models.py:782
#: apps/transactions/models.py:1032 templates/includes/sidebar.html:150
#: templates/insights/fragments/category_overview/index.html:40
#: templates/insights/fragments/month_by_month.html:29
#: templates/insights/fragments/month_by_month.html:32
#: templates/insights/fragments/year_by_year.html:24
#: templates/insights/fragments/year_by_year.html:27
#: templates/tags/fragments/list.html:9 templates/tags/pages/index.html:4
msgid "Tags"
msgstr "標籤"
@@ -92,7 +98,7 @@ msgstr "標籤"
#: apps/accounts/models.py:12 apps/accounts/models.py:29 apps/dca/models.py:13
#: apps/import_app/models.py:14 apps/rules/models.py:13
#: apps/transactions/models.py:214 apps/transactions/models.py:239
#: apps/transactions/models.py:263 apps/transactions/models.py:990
#: apps/transactions/models.py:263 apps/transactions/models.py:994
#: templates/account_groups/fragments/list.html:22
#: templates/accounts/fragments/list.html:22
#: templates/categories/fragments/table.html:17
@@ -161,8 +167,8 @@ msgstr "封存的帳戶不會在淨資產中被計算或顯示"
#: apps/transactions/forms.py:63 apps/transactions/forms.py:271
#: apps/transactions/forms.py:386 apps/transactions/forms.py:692
#: apps/transactions/forms.py:933 apps/transactions/models.py:294
#: apps/transactions/models.py:534 apps/transactions/models.py:756
#: apps/transactions/models.py:996
#: apps/transactions/models.py:538 apps/transactions/models.py:760
#: apps/transactions/models.py:1000
#: templates/installment_plans/fragments/table.html:17
#: templates/quick_transactions/fragments/list.html:14
#: templates/recurring_transactions/fragments/table.html:19
@@ -171,11 +177,11 @@ msgid "Account"
msgstr "帳戶"
#: apps/accounts/models.py:76 apps/export_app/forms.py:19
#: apps/export_app/forms.py:129 apps/transactions/filters.py:52
#: apps/export_app/forms.py:129 apps/transactions/filters.py:57
#: templates/accounts/fragments/list.html:9
#: templates/accounts/pages/index.html:4 templates/includes/sidebar.html:162
#: templates/includes/sidebar.html:164
#: templates/monthly_overview/pages/overview.html:75
#: templates/monthly_overview/pages/overview.html:77
#: templates/transactions/pages/transactions.html:26
msgid "Accounts"
msgstr "帳戶"
@@ -190,8 +196,8 @@ msgstr "成功新增帳戶組"
#: apps/accounts/views/account_groups.py:69
#: apps/accounts/views/account_groups.py:152 apps/accounts/views/accounts.py:68
#: apps/accounts/views/accounts.py:106 apps/dca/views.py:63
#: apps/dca/views.py:146 apps/rules/views.py:118 apps/rules/views.py:228
#: apps/accounts/views/accounts.py:106 apps/dca/views.py:62
#: apps/dca/views.py:145 apps/rules/views.py:118 apps/rules/views.py:228
#: apps/transactions/views/categories.py:91
#: apps/transactions/views/categories.py:129
#: apps/transactions/views/entities.py:91
@@ -205,7 +211,7 @@ msgid "Account Group updated successfully"
msgstr "成功更新帳戶組"
#: apps/accounts/views/account_groups.py:111
#: apps/accounts/views/accounts.py:145 apps/dca/views.py:105
#: apps/accounts/views/accounts.py:145 apps/dca/views.py:104
#: apps/rules/views.py:185 apps/transactions/views/categories.py:168
#: apps/transactions/views/entities.py:130 apps/transactions/views/tags.py:130
msgid "Item no longer shared with you"
@@ -216,14 +222,14 @@ msgid "Account Group deleted successfully"
msgstr "成功刪除帳戶組"
#: apps/accounts/views/account_groups.py:135
#: apps/accounts/views/accounts.py:189 apps/dca/views.py:129
#: apps/accounts/views/accounts.py:189 apps/dca/views.py:128
#: apps/rules/views.py:210 apps/transactions/views/categories.py:192
#: apps/transactions/views/entities.py:154 apps/transactions/views/tags.py:154
msgid "Ownership taken successfully"
msgstr "成功取得所有權"
#: apps/accounts/views/account_groups.py:165
#: apps/accounts/views/accounts.py:119 apps/dca/views.py:159
#: apps/accounts/views/accounts.py:119 apps/dca/views.py:158
#: apps/rules/views.py:241 apps/transactions/views/categories.py:142
#: apps/transactions/views/entities.py:184 apps/transactions/views/tags.py:184
msgid "Configuration saved successfully"
@@ -360,7 +366,7 @@ msgid "Public"
msgstr "公開"
#: apps/common/templatetags/natural.py:20
#: templates/monthly_overview/fragments/monthly_summary.html:9
#: templates/monthly_overview/fragments/monthly_summary.html:10
msgid "today"
msgstr "今天"
@@ -452,10 +458,10 @@ msgstr "移除"
#: apps/common/widgets/tom_select.py:15
#: templates/mini_tools/unit_price_calculator.html:180
#: templates/monthly_overview/pages/overview.html:171
#: templates/monthly_overview/pages/overview.html:183
#: templates/transactions/pages/transactions.html:124
#: templates/transactions/pages/transactions.html:136
#: templates/monthly_overview/pages/overview.html:275
#: templates/monthly_overview/pages/overview.html:287
#: templates/transactions/pages/transactions.html:225
#: templates/transactions/pages/transactions.html:237
msgid "Clear"
msgstr "清除"
@@ -494,11 +500,11 @@ msgid "Decimal Places"
msgstr "小數點後位數"
#: apps/currencies/models.py:45 apps/export_app/forms.py:25
#: apps/export_app/forms.py:130 apps/transactions/filters.py:59
#: apps/export_app/forms.py:130 apps/transactions/filters.py:64
#: templates/currencies/fragments/list.html:9
#: templates/currencies/pages/index.html:4 templates/includes/sidebar.html:176
#: templates/includes/sidebar.html:178
#: templates/monthly_overview/pages/overview.html:62
#: templates/monthly_overview/pages/overview.html:63
#: templates/transactions/pages/transactions.html:12
msgid "Currencies"
msgstr "貨幣"
@@ -559,9 +565,9 @@ msgstr "服務名稱"
msgid "Service Type"
msgstr "服務類型"
#: apps/currencies/models.py:118 apps/transactions/models.py:218
#: apps/transactions/models.py:242 apps/transactions/models.py:266
#: templates/categories/fragments/list.html:16
#: apps/currencies/models.py:118 apps/transactions/filters.py:27
#: apps/transactions/models.py:218 apps/transactions/models.py:242
#: apps/transactions/models.py:266 templates/categories/fragments/list.html:16
#: templates/entities/fragments/list.html:16
#: templates/installment_plans/fragments/list.html:16
#: templates/recurring_transactions/fragments/list.html:16
@@ -589,51 +595,51 @@ msgstr "間隔"
msgid "Last Successful Fetch"
msgstr "最後更新時間"
#: apps/currencies/models.py:141
#: apps/currencies/models.py:143
msgid "Target Currencies"
msgstr "目標貨幣"
#: apps/currencies/models.py:143
#: apps/currencies/models.py:145
msgid ""
"Select currencies to fetch exchange rates for. Rates will be fetched for "
"each currency against their set exchange currency."
msgstr "選擇要自動擷取匯率的貨幣,貨幣會根據設定的目標貨幣自動取得匯率資訊。"
#: apps/currencies/models.py:151
#: apps/currencies/models.py:153
msgid "Target Accounts"
msgstr "目標帳戶"
#: apps/currencies/models.py:153
#: apps/currencies/models.py:155
msgid ""
"Select accounts to fetch exchange rates for. Rates will be fetched for each "
"account's currency against their set exchange currency."
msgstr "選擇自動擷取匯率的帳戶,帳戶會根據設定的匯兌貨幣取得匯率資訊。"
#: apps/currencies/models.py:160
#: apps/currencies/models.py:162
msgid "Single exchange rate"
msgstr "保留單一匯率資訊"
#: apps/currencies/models.py:163
#: apps/currencies/models.py:165
msgid "Create one exchange rate and keep updating it. Avoids database clutter."
msgstr "只建立一筆匯率資訊並且持續更新,防止資料庫無限擴張。"
#: apps/currencies/models.py:168
#: apps/currencies/models.py:170
msgid "Exchange Rate Service"
msgstr "匯率資訊服務"
#: apps/currencies/models.py:169
#: apps/currencies/models.py:171
msgid "Exchange Rate Services"
msgstr "匯率資訊服務"
#: apps/currencies/models.py:221
#: apps/currencies/models.py:223
msgid "'Every X hours' interval type requires a positive integer."
msgstr "「每X小時」需要提供正整數。"
#: apps/currencies/models.py:230
#: apps/currencies/models.py:232
msgid "'Every X hours' interval must be between 1 and 24."
msgstr "「每X小時」需要介於1到24之間。"
#: apps/currencies/models.py:244
#: apps/currencies/models.py:246
msgid ""
"Invalid hour format. Use comma-separated hours (0-23) and/or ranges (e.g., "
"'1-5,8,10-12')."
@@ -641,7 +647,7 @@ msgstr ""
"錯誤的小時格式請使用逗號設定多個小時0~23或著設定範圍例"
"如:'1-5,10-12')。"
#: apps/currencies/models.py:255
#: apps/currencies/models.py:257
msgid ""
"Invalid format. Please check the requirements for your selected interval "
"type."
@@ -740,8 +746,8 @@ msgstr "交易貨幣"
#: apps/dca/models.py:26 apps/dca/models.py:181 apps/rules/forms.py:180
#: apps/rules/forms.py:196 apps/rules/models.py:43 apps/rules/models.py:295
#: apps/transactions/forms.py:413 apps/transactions/forms.py:560
#: apps/transactions/models.py:318 apps/transactions/models.py:583
#: apps/transactions/models.py:784 apps/transactions/models.py:1018
#: apps/transactions/models.py:318 apps/transactions/models.py:587
#: apps/transactions/models.py:788 apps/transactions/models.py:1022
msgid "Notes"
msgstr "備註"
@@ -773,27 +779,27 @@ msgstr "定期定額投入"
msgid "DCA Entries"
msgstr "定期定額投入"
#: apps/dca/views.py:39
#: apps/dca/views.py:38
msgid "DCA Strategy added successfully"
msgstr "成功新增定期定額策略"
#: apps/dca/views.py:76
#: apps/dca/views.py:75
msgid "DCA Strategy updated successfully"
msgstr "成功更新定期定額策略"
#: apps/dca/views.py:108
#: apps/dca/views.py:107
msgid "DCA strategy deleted successfully"
msgstr "成功刪除定期定額策略"
#: apps/dca/views.py:238
#: apps/dca/views.py:237
msgid "Entry added successfully"
msgstr "成功新增投入"
#: apps/dca/views.py:265
#: apps/dca/views.py:264
msgid "Entry updated successfully"
msgstr "成功更新投入"
#: apps/dca/views.py:291
#: apps/dca/views.py:290
msgid "Entry deleted successfully"
msgstr "成功刪除投入"
@@ -813,34 +819,42 @@ msgid "Transactions"
msgstr "交易"
#: apps/export_app/forms.py:37 apps/export_app/forms.py:131
#: apps/transactions/filters.py:63 templates/categories/fragments/list.html:9
#: apps/transactions/filters.py:68 templates/categories/fragments/list.html:9
#: templates/categories/pages/index.html:4 templates/includes/sidebar.html:144
#: templates/insights/fragments/month_by_month.html:18
#: templates/insights/fragments/month_by_month.html:21
#: templates/insights/fragments/year_by_year.html:13
#: templates/insights/fragments/year_by_year.html:16
msgid "Categories"
msgstr "類別"
#: apps/export_app/forms.py:49 apps/export_app/forms.py:133
#: apps/rules/forms.py:185 apps/rules/forms.py:195 apps/rules/models.py:46
#: apps/rules/models.py:307 apps/transactions/filters.py:73
#: apps/rules/models.py:307 apps/transactions/filters.py:78
#: apps/transactions/forms.py:59 apps/transactions/forms.py:267
#: apps/transactions/forms.py:435 apps/transactions/forms.py:715
#: apps/transactions/forms.py:956 apps/transactions/models.py:277
#: apps/transactions/models.py:333 apps/transactions/models.py:579
#: apps/transactions/models.py:781 apps/transactions/models.py:1033
#: apps/transactions/models.py:333 apps/transactions/models.py:583
#: apps/transactions/models.py:785 apps/transactions/models.py:1037
#: templates/entities/fragments/list.html:9
#: templates/entities/pages/index.html:4 templates/includes/sidebar.html:156
#: templates/insights/fragments/category_overview/index.html:54
#: templates/insights/fragments/month_by_month.html:40
#: templates/insights/fragments/month_by_month.html:43
#: templates/insights/fragments/year_by_year.html:35
#: templates/insights/fragments/year_by_year.html:38
msgid "Entities"
msgstr "實體"
#: apps/export_app/forms.py:55 apps/export_app/forms.py:137
#: apps/transactions/models.py:821 templates/includes/sidebar.html:110
#: apps/transactions/models.py:825 templates/includes/sidebar.html:110
#: templates/recurring_transactions/fragments/list.html:9
#: templates/recurring_transactions/pages/index.html:4
msgid "Recurring Transactions"
msgstr "定期扣款交易"
#: apps/export_app/forms.py:61 apps/export_app/forms.py:135
#: apps/transactions/models.py:597 templates/includes/sidebar.html:104
#: apps/transactions/models.py:601 templates/includes/sidebar.html:104
#: templates/installment_plans/fragments/list.html:9
#: templates/installment_plans/pages/index.html:4
msgid "Installment Plans"
@@ -992,10 +1006,12 @@ msgid "Run deleted successfully"
msgstr "成功刪除匯入任務"
#: apps/insights/forms.py:118 apps/insights/utils/sankey.py:36
#: apps/insights/utils/sankey.py:167 apps/transactions/filters.py:186
#: apps/insights/utils/sankey.py:167 apps/transactions/filters.py:203
#: templates/insights/fragments/category_overview/index.html:96
#: templates/insights/fragments/category_overview/index.html:407
#: templates/insights/fragments/category_overview/index.html:436
#: templates/insights/fragments/month_by_month.html:119
#: templates/insights/fragments/year_by_year.html:73
msgid "Uncategorized"
msgstr "未分類"
@@ -1088,15 +1104,15 @@ msgstr "運算子"
#: apps/rules/forms.py:174 apps/rules/forms.py:188 apps/rules/models.py:36
#: apps/rules/models.py:271 apps/transactions/forms.py:377
#: apps/transactions/models.py:301 apps/transactions/models.py:539
#: apps/transactions/models.py:762 apps/transactions/models.py:1003
#: apps/transactions/models.py:301 apps/transactions/models.py:543
#: apps/transactions/models.py:766 apps/transactions/models.py:1007
msgid "Type"
msgstr "種類"
#: apps/rules/forms.py:175 apps/rules/forms.py:189 apps/rules/models.py:37
#: apps/rules/models.py:275 apps/transactions/filters.py:22
#: apps/transactions/forms.py:381 apps/transactions/models.py:303
#: apps/transactions/models.py:1005 templates/cotton/transaction/item.html:20
#: apps/transactions/models.py:1009 templates/cotton/transaction/item.html:20
#: templates/cotton/transaction/item.html:31
#: templates/transactions/widgets/paid_toggle_button.html:10
#: templates/transactions/widgets/unselectable_paid_toggle_button.html:13
@@ -1107,14 +1123,14 @@ msgstr "已支付"
#: apps/rules/models.py:283 apps/transactions/forms.py:71
#: apps/transactions/forms.py:397 apps/transactions/forms.py:547
#: apps/transactions/forms.py:721 apps/transactions/models.py:305
#: apps/transactions/models.py:557 apps/transactions/models.py:786
#: apps/transactions/models.py:561 apps/transactions/models.py:790
msgid "Reference Date"
msgstr "起算日"
#: apps/rules/forms.py:178 apps/rules/forms.py:192 apps/rules/models.py:41
#: apps/rules/models.py:287 apps/transactions/forms.py:404
#: apps/transactions/models.py:311 apps/transactions/models.py:767
#: apps/transactions/models.py:1011
#: apps/transactions/models.py:311 apps/transactions/models.py:771
#: apps/transactions/models.py:1015
#: templates/insights/fragments/sankey.html:102
#: templates/installment_plans/fragments/table.html:18
#: templates/quick_transactions/fragments/list.html:15
@@ -1125,27 +1141,27 @@ msgstr "金額"
#: apps/rules/forms.py:179 apps/rules/forms.py:193 apps/rules/models.py:14
#: apps/rules/models.py:42 apps/rules/models.py:291
#: apps/transactions/forms.py:408 apps/transactions/forms.py:551
#: apps/transactions/models.py:316 apps/transactions/models.py:541
#: apps/transactions/models.py:770 apps/transactions/models.py:1016
#: apps/transactions/models.py:316 apps/transactions/models.py:545
#: apps/transactions/models.py:774 apps/transactions/models.py:1020
msgid "Description"
msgstr "描述"
#: apps/rules/forms.py:182 apps/rules/forms.py:198 apps/rules/models.py:47
#: apps/rules/models.py:299 apps/transactions/models.py:355
#: apps/transactions/models.py:1038
#: apps/transactions/models.py:1042
msgid "Internal Note"
msgstr "內部註記"
#: apps/rules/forms.py:183 apps/rules/forms.py:199 apps/rules/models.py:48
#: apps/rules/models.py:303 apps/transactions/models.py:357
#: apps/transactions/models.py:1040
#: apps/transactions/models.py:1044
msgid "Internal ID"
msgstr "內部ID"
#: apps/rules/forms.py:186 apps/rules/forms.py:200 apps/rules/models.py:40
#: apps/rules/models.py:319 apps/transactions/forms.py:564
#: apps/transactions/models.py:215 apps/transactions/models.py:306
#: apps/transactions/models.py:1006
#: apps/transactions/models.py:1010
msgid "Mute"
msgstr "靜音"
@@ -1301,53 +1317,67 @@ msgstr "成功刪除「更新或建立交易行為」"
msgid "Projected"
msgstr "預期"
#: apps/transactions/filters.py:40
#: apps/transactions/filters.py:28 templates/categories/fragments/table.html:18
msgid "Muted"
msgstr "已靜音"
#: apps/transactions/filters.py:45
msgid "Content"
msgstr "內容"
#: apps/transactions/filters.py:46
#: apps/transactions/filters.py:51
msgid "Transaction Type"
msgstr "交易種類"
#: apps/transactions/filters.py:84
#: apps/transactions/filters.py:89
#, fuzzy
#| msgid "Status"
msgid "Mute Status"
msgstr "狀態"
#: apps/transactions/filters.py:94
msgid "Date from"
msgstr "起始日期"
#: apps/transactions/filters.py:89 apps/transactions/filters.py:99
#: apps/transactions/filters.py:99 apps/transactions/filters.py:109
msgid "Until"
msgstr "直到"
#: apps/transactions/filters.py:94
#: apps/transactions/filters.py:104
msgid "Reference date from"
msgstr "起算日日期"
#: apps/transactions/filters.py:104
#: apps/transactions/filters.py:114
msgid "Amount min"
msgstr "最小金額"
#: apps/transactions/filters.py:109
#: apps/transactions/filters.py:119
msgid "Amount max"
msgstr "最大金額"
#: apps/transactions/filters.py:185
#: apps/transactions/filters.py:202
msgid "Categorized"
msgstr "已分類"
#: apps/transactions/filters.py:192
#: apps/transactions/filters.py:209
msgid "Tagged"
msgstr "已標籤"
#: apps/transactions/filters.py:192
#: apps/transactions/filters.py:209
#: templates/insights/fragments/category_overview/index.html:189
#: templates/insights/fragments/month_by_month.html:121
#: templates/insights/fragments/year_by_year.html:75
msgid "Untagged"
msgstr "未標籤"
#: apps/transactions/filters.py:198
#: apps/transactions/filters.py:215
msgid "Any entity"
msgstr "任何實體"
#: apps/transactions/filters.py:199
#: apps/transactions/filters.py:216
#: templates/insights/fragments/category_overview/index.html:282
#: templates/insights/fragments/month_by_month.html:123
#: templates/insights/fragments/year_by_year.html:77
msgid "No entity"
msgstr "無實體"
@@ -1435,10 +1465,12 @@ msgid ""
msgstr "新增交易的時候無法選擇停用的實體"
#: apps/transactions/models.py:276
#: templates/insights/fragments/month_by_month.html:88
#: templates/insights/fragments/year_by_year.html:56
msgid "Entity"
msgstr "實體"
#: apps/transactions/models.py:288 apps/transactions/models.py:983
#: apps/transactions/models.py:288 apps/transactions/models.py:987
#: templates/calendar_view/fragments/list.html:42
#: templates/calendar_view/fragments/list.html:44
#: templates/calendar_view/fragments/list.html:52
@@ -1446,11 +1478,11 @@ msgstr "實體"
#: templates/cotton/ui/quick_transactions_buttons.html:10
#: templates/cotton/ui/transactions_fab.html:10
#: templates/insights/fragments/category_overview/index.html:87
#: templates/monthly_overview/fragments/monthly_summary.html:39
#: templates/monthly_overview/fragments/monthly_summary.html:41
msgid "Income"
msgstr "收入"
#: apps/transactions/models.py:289 apps/transactions/models.py:984
#: apps/transactions/models.py:289 apps/transactions/models.py:988
#: templates/calendar_view/fragments/list.html:46
#: templates/calendar_view/fragments/list.html:48
#: templates/calendar_view/fragments/list.html:56
@@ -1461,11 +1493,11 @@ msgstr "收入"
msgid "Expense"
msgstr "支出"
#: apps/transactions/models.py:344 apps/transactions/models.py:596
#: apps/transactions/models.py:344 apps/transactions/models.py:600
msgid "Installment Plan"
msgstr "分期付款計劃"
#: apps/transactions/models.py:353 apps/transactions/models.py:820
#: apps/transactions/models.py:353 apps/transactions/models.py:824
msgid "Recurring Transaction"
msgstr "定期扣款交易"
@@ -1477,113 +1509,113 @@ msgstr "已刪除"
msgid "Deleted At"
msgstr "刪除時間"
#: apps/transactions/models.py:476 templates/tags/fragments/table.html:69
#: apps/transactions/models.py:480 templates/tags/fragments/table.html:69
msgid "No tags"
msgstr "沒有標籤"
#: apps/transactions/models.py:478
#: apps/transactions/models.py:482
msgid "No category"
msgstr "沒有分類"
#: apps/transactions/models.py:480
#: apps/transactions/models.py:484
msgid "No description"
msgstr "沒有描述"
#: apps/transactions/models.py:528 templates/includes/sidebar.html:57
#: apps/transactions/models.py:532 templates/includes/sidebar.html:57
msgid "Yearly"
msgstr "年"
#: apps/transactions/models.py:529 apps/users/models.py:464
#: apps/transactions/models.py:533 apps/users/models.py:464
#: templates/includes/sidebar.html:51
msgid "Monthly"
msgstr "月"
#: apps/transactions/models.py:530
#: apps/transactions/models.py:534
msgid "Weekly"
msgstr "週"
#: apps/transactions/models.py:531
#: apps/transactions/models.py:535
msgid "Daily"
msgstr "日"
#: apps/transactions/models.py:544
#: apps/transactions/models.py:548
msgid "Number of Installments"
msgstr "期數"
#: apps/transactions/models.py:549
#: apps/transactions/models.py:553
msgid "Installment Start"
msgstr "分期付款起始日"
#: apps/transactions/models.py:550
#: apps/transactions/models.py:554
msgid "The installment number to start counting from"
msgstr "開始計算的期數"
#: apps/transactions/models.py:555 apps/transactions/models.py:790
#: apps/transactions/models.py:559 apps/transactions/models.py:794
msgid "Start Date"
msgstr "起始日期"
#: apps/transactions/models.py:559 apps/transactions/models.py:791
#: apps/transactions/models.py:563 apps/transactions/models.py:795
msgid "End Date"
msgstr "結束日期"
#: apps/transactions/models.py:564
#: apps/transactions/models.py:568
msgid "Recurrence"
msgstr "頻率"
#: apps/transactions/models.py:567
#: apps/transactions/models.py:571
msgid "Installment Amount"
msgstr "分期付款金額"
#: apps/transactions/models.py:586 apps/transactions/models.py:810
#: apps/transactions/models.py:590 apps/transactions/models.py:814
msgid "Add description to transactions"
msgstr "為交易增加描述"
#: apps/transactions/models.py:589 apps/transactions/models.py:813
#: apps/transactions/models.py:593 apps/transactions/models.py:817
msgid "Add notes to transactions"
msgstr "為交易新增註記"
#: apps/transactions/models.py:749
#: apps/transactions/models.py:753
msgid "day(s)"
msgstr "天"
#: apps/transactions/models.py:750
#: apps/transactions/models.py:754
msgid "week(s)"
msgstr "週"
#: apps/transactions/models.py:751
#: apps/transactions/models.py:755
msgid "month(s)"
msgstr "月"
#: apps/transactions/models.py:752
#: apps/transactions/models.py:756
msgid "year(s)"
msgstr "年"
#: apps/transactions/models.py:754
#: apps/transactions/models.py:758
#: templates/recurring_transactions/fragments/list.html:18
msgid "Paused"
msgstr "已暫停"
#: apps/transactions/models.py:793
#: apps/transactions/models.py:797
msgid "Recurrence Type"
msgstr "頻率"
#: apps/transactions/models.py:796
#: apps/transactions/models.py:800
msgid "Recurrence Interval"
msgstr "頻率間隔"
#: apps/transactions/models.py:799
#: apps/transactions/models.py:803
msgid "Keep at most"
msgstr "持續最多"
#: apps/transactions/models.py:803
#: apps/transactions/models.py:807
msgid "Last Generated Date"
msgstr "最後產生的日期"
#: apps/transactions/models.py:806
#: apps/transactions/models.py:810
msgid "Last Generated Reference Date"
msgstr "最後產生的起算日"
#: apps/transactions/models.py:1050
#: apps/transactions/models.py:1054
#: apps/transactions/views/quick_transactions.py:178
#: apps/transactions/views/quick_transactions.py:187
#: apps/transactions/views/quick_transactions.py:189
@@ -1592,7 +1624,7 @@ msgstr "最後產生的起算日"
msgid "Quick Transaction"
msgstr "快速交易"
#: apps/transactions/models.py:1051 templates/includes/sidebar.html:98
#: apps/transactions/models.py:1055 templates/includes/sidebar.html:98
#: templates/quick_transactions/pages/index.html:5
#: templates/quick_transactions/pages/index.html:15
msgid "Quick Transactions"
@@ -1693,7 +1725,7 @@ msgstr "成功刪除項目"
#: apps/transactions/views/quick_transactions.py:156
#: apps/transactions/views/transactions.py:53
#: apps/transactions/views/transactions.py:228
#: apps/transactions/views/transactions.py:238
msgid "Transaction added successfully"
msgstr "成功新增交易"
@@ -1733,29 +1765,29 @@ msgstr "成功更新標籤"
msgid "Tag deleted successfully"
msgstr "成功刪除標籤"
#: apps/transactions/views/transactions.py:252
#: apps/transactions/views/transactions.py:262
msgid "Transaction updated successfully"
msgstr "成功更新交易"
#: apps/transactions/views/transactions.py:303
#: apps/transactions/views/transactions.py:313
#, python-format
msgid "%(count)s transaction updated successfully"
msgid_plural "%(count)s transactions updated successfully"
msgstr[0] "成功更新%(count)s筆交易"
#: apps/transactions/views/transactions.py:339
#: apps/transactions/views/transactions.py:349
msgid "Transaction duplicated successfully"
msgstr "成功複製交易"
#: apps/transactions/views/transactions.py:381
#: apps/transactions/views/transactions.py:391
msgid "Transaction deleted successfully"
msgstr "成功刪除交易"
#: apps/transactions/views/transactions.py:399
#: apps/transactions/views/transactions.py:409
msgid "Transaction restored successfully"
msgstr "成功復原交易"
#: apps/transactions/views/transactions.py:425
#: apps/transactions/views/transactions.py:435
msgid "Transfer added successfully"
msgstr "成功新增轉帳"
@@ -1797,10 +1829,10 @@ msgid "This account is deactivated"
msgstr "這個帳號已經被停用"
#: apps/users/forms.py:62 apps/users/forms.py:75 apps/users/forms.py:97
#: templates/monthly_overview/pages/overview.html:95
#: templates/monthly_overview/pages/overview.html:141
#: templates/monthly_overview/pages/overview.html:98
#: templates/monthly_overview/pages/overview.html:245
#: templates/transactions/pages/transactions.html:47
#: templates/transactions/pages/transactions.html:94
#: templates/transactions/pages/transactions.html:195
msgid "Default"
msgstr "預設"
@@ -2222,10 +2254,6 @@ msgstr "新增分類"
msgid "Edit category"
msgstr "編輯分類"
#: templates/categories/fragments/table.html:18
msgid "Muted"
msgstr "已靜音"
#: templates/categories/fragments/table.html:73
#: templates/insights/fragments/category_overview/index.html:552
msgid "No categories"
@@ -2244,9 +2272,9 @@ msgstr "關閉"
#: templates/cotton/config/search.html:6
#: templates/import_app/fragments/profiles/list_presets.html:13
#: templates/monthly_overview/pages/overview.html:115
#: templates/monthly_overview/pages/overview.html:219
#: templates/rules/fragments/transaction_rule/dry_run/visual.html:57
#: templates/transactions/pages/transactions.html:67
#: templates/transactions/pages/transactions.html:168
msgid "Search"
msgstr "搜尋"
@@ -2480,8 +2508,8 @@ msgid "No entries for this DCA"
msgstr "這個定期定額沒有投入"
#: templates/dca/fragments/strategy/details.html:120
#: templates/monthly_overview/fragments/list.html:33
#: templates/transactions/fragments/list_all.html:33
#: templates/monthly_overview/fragments/list.html:59
#: templates/transactions/fragments/list_all.html:59
msgid "Try adding one"
msgstr "試著增加一個"
@@ -2606,7 +2634,7 @@ msgstr "沒有匯率"
#: templates/exchange_rates/fragments/table.html:56
#: templates/exchange_rates_services/fragments/table.html:57
#: templates/transactions/fragments/list_all.html:43
#: templates/transactions/fragments/list_all.html:70
msgid "Page navigation"
msgstr "頁面導覽"
@@ -2626,15 +2654,21 @@ msgstr "目標"
msgid "Last fetch"
msgstr "最後更新"
#: templates/exchange_rates_services/fragments/list.html:61
#: templates/exchange_rates_services/fragments/list.html:62
#, python-format
msgid "%(counter)s consecutive failure"
msgid_plural "%(counter)s consecutive failures"
msgstr[0] ""
#: templates/exchange_rates_services/fragments/list.html:69
msgid "currencies"
msgstr "貨幣"
#: templates/exchange_rates_services/fragments/list.html:61
#: templates/exchange_rates_services/fragments/list.html:69
msgid "accounts"
msgstr "帳戶"
#: templates/exchange_rates_services/fragments/list.html:69
#: templates/exchange_rates_services/fragments/list.html:77
msgid "No services configured"
msgstr "沒有設定任何服務"
@@ -2884,7 +2918,11 @@ msgid "Final total"
msgstr "最終總額"
#: templates/insights/fragments/category_overview/index.html:89
#: templates/monthly_overview/fragments/monthly_summary.html:165
#: templates/insights/fragments/month_by_month.html:91
#: templates/insights/fragments/month_by_month.html:186
#: templates/insights/fragments/year_by_year.html:59
#: templates/insights/fragments/year_by_year.html:140
#: templates/monthly_overview/fragments/monthly_summary.html:167
msgid "Total"
msgstr "總額"
@@ -2928,6 +2966,75 @@ msgstr "沒有逾期交易"
msgid "No recent transactions"
msgstr "沒有近期交易"
#: templates/insights/fragments/month_by_month.html:86
#: templates/insights/fragments/year_by_year.html:54
#, fuzzy
#| msgid "Tags"
msgid "Tag"
msgstr "標籤"
#: templates/insights/fragments/month_by_month.html:94
msgid "Jan"
msgstr ""
#: templates/insights/fragments/month_by_month.html:95
msgid "Feb"
msgstr ""
#: templates/insights/fragments/month_by_month.html:96
#, fuzzy
#| msgid "Max"
msgid "Mar"
msgstr "最大值"
#: templates/insights/fragments/month_by_month.html:97
msgid "Apr"
msgstr ""
#: templates/insights/fragments/month_by_month.html:98
#, fuzzy
#| msgid "Max"
msgid "May"
msgstr "最大值"
#: templates/insights/fragments/month_by_month.html:99
msgid "Jun"
msgstr ""
#: templates/insights/fragments/month_by_month.html:100
msgid "Jul"
msgstr ""
#: templates/insights/fragments/month_by_month.html:101
msgid "Aug"
msgstr ""
#: templates/insights/fragments/month_by_month.html:102
#, fuzzy
#| msgid "Set"
msgid "Sep"
msgstr "設定"
#: templates/insights/fragments/month_by_month.html:103
msgid "Oct"
msgstr ""
#: templates/insights/fragments/month_by_month.html:104
#, fuzzy
#| msgid "Now"
msgid "Nov"
msgstr "現在"
#: templates/insights/fragments/month_by_month.html:105
msgid "Dec"
msgstr ""
#: templates/insights/fragments/month_by_month.html:248
#, fuzzy
#| msgid "No transactions on this date"
msgid "No transactions for this year"
msgstr "本日沒有交易"
#: templates/insights/fragments/sankey.html:100
msgid "From"
msgstr "從"
@@ -2936,6 +3043,12 @@ msgstr "從"
msgid "Percentage"
msgstr "百分比"
#: templates/insights/fragments/year_by_year.html:202
#, fuzzy
#| msgid "transactions"
msgid "No transactions"
msgstr "交易"
#: templates/insights/pages/index.html:37
msgid "Month"
msgstr "月"
@@ -2985,6 +3098,16 @@ msgstr "最新的交易"
msgid "Emergency Fund"
msgstr "緊急資金"
#: templates/insights/pages/index.html:127
#, fuzzy
#| msgid "Yearly by account"
msgid "Year by Year"
msgstr "以帳戶為主的年報"
#: templates/insights/pages/index.html:132
msgid "Month by Month"
msgstr ""
#: templates/installment_plans/fragments/add.html:5
msgid "Add installment plan"
msgstr "新增分期付款計劃"
@@ -3056,35 +3179,40 @@ msgstr "單位價格"
msgid "Item"
msgstr "項目"
#: templates/monthly_overview/fragments/list.html:32
#: templates/monthly_overview/fragments/list.html:15
#: templates/transactions/fragments/list_all.html:15
msgid "late"
msgstr ""
#: templates/monthly_overview/fragments/list.html:58
msgid "No transactions this month"
msgstr "這個月沒有交易"
#: templates/monthly_overview/fragments/monthly_summary.html:6
#: templates/monthly_overview/fragments/monthly_summary.html:7
msgid "Daily Spending Allowance"
msgstr "每日支出上限"
#: templates/monthly_overview/fragments/monthly_summary.html:6
#: templates/monthly_overview/fragments/monthly_summary.html:7
msgid "This is the final total divided by the remaining days in the month"
msgstr "這是最終總額除以這個月的剩餘天數所計算"
#: templates/monthly_overview/fragments/monthly_summary.html:42
#: templates/monthly_overview/fragments/monthly_summary.html:105
#: templates/monthly_overview/fragments/monthly_summary.html:168
#: templates/monthly_overview/fragments/monthly_summary.html:44
#: templates/monthly_overview/fragments/monthly_summary.html:107
#: templates/monthly_overview/fragments/monthly_summary.html:170
msgid "current"
msgstr "目前"
#: templates/monthly_overview/fragments/monthly_summary.html:71
#: templates/monthly_overview/fragments/monthly_summary.html:134
#: templates/monthly_overview/fragments/monthly_summary.html:197
#: templates/monthly_overview/fragments/monthly_summary.html:73
#: templates/monthly_overview/fragments/monthly_summary.html:136
#: templates/monthly_overview/fragments/monthly_summary.html:199
msgid "projected"
msgstr "預期的"
#: templates/monthly_overview/fragments/monthly_summary.html:102
#: templates/monthly_overview/fragments/monthly_summary.html:104
msgid "Expenses"
msgstr "支出"
#: templates/monthly_overview/fragments/monthly_summary.html:255
#: templates/monthly_overview/fragments/monthly_summary.html:257
msgid "Distribution"
msgstr "分佈"
@@ -3092,27 +3220,27 @@ msgstr "分佈"
msgid "Summary"
msgstr "總結"
#: templates/monthly_overview/pages/overview.html:96
#: templates/monthly_overview/pages/overview.html:150
#: templates/monthly_overview/pages/overview.html:99
#: templates/monthly_overview/pages/overview.html:254
#: templates/transactions/pages/transactions.html:48
#: templates/transactions/pages/transactions.html:103
#: templates/transactions/pages/transactions.html:204
msgid "Oldest first"
msgstr "最舊的優先"
#: templates/monthly_overview/pages/overview.html:97
#: templates/monthly_overview/pages/overview.html:159
#: templates/monthly_overview/pages/overview.html:100
#: templates/monthly_overview/pages/overview.html:263
#: templates/transactions/pages/transactions.html:49
#: templates/transactions/pages/transactions.html:112
#: templates/transactions/pages/transactions.html:213
msgid "Newest first"
msgstr "新的優先"
#: templates/monthly_overview/pages/overview.html:106
#: templates/monthly_overview/pages/overview.html:109
#: templates/transactions/pages/transactions.html:58
msgid "Filter transactions"
msgstr "過濾交易"
#: templates/monthly_overview/pages/overview.html:131
#: templates/transactions/pages/transactions.html:84
#: templates/monthly_overview/pages/overview.html:235
#: templates/transactions/pages/transactions.html:185
msgid "Order by"
msgstr "排序方式"
@@ -3355,7 +3483,7 @@ msgstr "編輯"
msgid "transactions"
msgstr "交易"
#: templates/transactions/fragments/list_all.html:32
#: templates/transactions/fragments/list_all.html:58
msgid "No transactions found"
msgstr "沒有發現交易"

View File

@@ -56,7 +56,15 @@
</td>
<td class="table-col-auto">{% if service.is_active %}<i class="fa-solid fa-circle text-success"></i>{% else %}
<i class="fa-solid fa-circle text-error"></i>{% endif %}</td>
<td class="table-col-auto">{{ service.name }}</td>
<td>
{{ service.name }}
{% if service.failure_count > 0 %}
<span class="badge badge-error gap-1" data-tippy-content="{% blocktrans count counter=service.failure_count %}{{ counter }} consecutive failure{% plural %}{{ counter }} consecutive failures{% endblocktrans %}">
<i class="fa-solid fa-triangle-exclamation fa-fw"></i>
{{ service.failure_count }}
</span>
{% endif %}
</td>
<td>{{ service.get_service_type_display }}</td>
<td>{{ service.target_currencies.count }} {% trans 'currencies' %}, {{ service.target_accounts.count }} {% trans 'accounts' %}</td>
<td>{{ service.last_fetch|date:"SHORT_DATETIME_FORMAT" }}</td>

View File

@@ -0,0 +1,250 @@
{% load i18n %}
<div hx-get="{% url 'insights_month_by_month' %}" hx-trigger="updated from:window" class="show-loading"
hx-swap="outerHTML" hx-include="#year-selector, #group-by-selector-month">
{# Hidden input to hold the year value #}
<input type="hidden" name="year" id="year-selector" value="{{ selected_year }}" _="on change trigger updated">
{# Tabs for Categories/Tags/Entities #}
<div class="h-full text-center mb-4">
<div class="tabs tabs-box mx-auto w-fit" role="group" id="group-by-selector-month" _="on change trigger updated">
<label class="tab">
<input type="radio"
name="group_by"
id="categories-view-month"
autocomplete="off"
value="categories"
aria-label="{% trans 'Categories' %}"
{% if group_by == "categories" %}checked{% endif %}>
<i class="fa-solid fa-icons fa-fw me-2"></i>
{% trans 'Categories' %}
</label>
<label class="tab">
<input type="radio"
name="group_by"
id="tags-view-month"
autocomplete="off"
value="tags"
aria-label="{% trans 'Tags' %}"
{% if group_by == "tags" %}checked{% endif %}>
<i class="fa-solid fa-hashtag fa-fw me-2"></i>
{% trans 'Tags' %}
</label>
<label class="tab">
<input type="radio"
name="group_by"
id="entities-view-month"
autocomplete="off"
value="entities"
aria-label="{% trans 'Entities' %}"
{% if group_by == "entities" %}checked{% endif %}>
<i class="fa-solid fa-user-group fa-fw me-2"></i>
{% trans 'Entities' %}
</label>
</div>
</div>
{% if data.items %}
<div class="card bg-base-100 card-border">
<div class="card-body">
{# Year dropdown - left aligned #}
{% if data.available_years %}
<div class="mb-4">
<div>
<button class="btn btn-ghost" type="button"
data-bs-toggle="dropdown" aria-expanded="false">
<i class="fa-solid fa-calendar fa-fw me-1"></i>
{{ selected_year }}
<i class="fa-solid fa-chevron-down fa-fw ms-1"></i>
</button>
<ul class="dropdown-menu menu">
{% for year in data.available_years %}
<li>
<button class="{% if year == selected_year %}menu-active{% endif %}" type="button"
_="on click remove .menu-active from <li > button/> in the closest <ul/>
then add .menu-active to me
then set the value of #year-selector to '{{ year }}'
then trigger change on #year-selector">
{{ year }}
</button>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
<div class="overflow-x-auto">
<table class="table">
<thead>
<tr>
<th scope="col" class="sticky left-0 bg-base-100 z-10">
{% if group_by == "categories" %}
{% trans 'Category' %}
{% elif group_by == "tags" %}
{% trans 'Tag' %}
{% else %}
{% trans 'Entity' %}
{% endif %}
</th>
<th scope="col" class="font-bold">{% trans 'Total' %}</th>
{% for month in data.months %}
<th scope="col">
{% if month == 1 %}{% trans 'Jan' %}
{% elif month == 2 %}{% trans 'Feb' %}
{% elif month == 3 %}{% trans 'Mar' %}
{% elif month == 4 %}{% trans 'Apr' %}
{% elif month == 5 %}{% trans 'May' %}
{% elif month == 6 %}{% trans 'Jun' %}
{% elif month == 7 %}{% trans 'Jul' %}
{% elif month == 8 %}{% trans 'Aug' %}
{% elif month == 9 %}{% trans 'Sep' %}
{% elif month == 10 %}{% trans 'Oct' %}
{% elif month == 11 %}{% trans 'Nov' %}
{% elif month == 12 %}{% trans 'Dec' %}
{% endif %}
</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for item_id, item in data.items.items %}
<tr>
<th class="text-nowrap sticky left-0 bg-base-100 z-10">
{% if item.name %}
{{ item.name }}
{% else %}
{% if group_by == "categories" %}
{% trans 'Uncategorized' %}
{% elif group_by == "tags" %}
{% trans 'Untagged' %}
{% else %}
{% trans 'No entity' %}
{% endif %}
{% endif %}
</th>
{# Total column for this item #}
<td class="text-nowrap font-semibold bg-base-200">
{% for currency_id, currency_data in item.total.currencies.items %}
<c-amount.display
:amount="currency_data.final_total"
:prefix="currency_data.currency.prefix"
:suffix="currency_data.currency.suffix"
:decimal_places="currency_data.currency.decimal_places"
color="{% if currency_data.final_total < 0 %}red{% elif currency_data.final_total > 0 %}green{% endif %}"></c-amount.display>
{% if currency_data.exchanged %}
<div class="text-xs text-base-content/60">
<c-amount.display
:amount="currency_data.exchanged.final_total"
:prefix="currency_data.exchanged.currency.prefix"
:suffix="currency_data.exchanged.currency.suffix"
:decimal_places="currency_data.exchanged.currency.decimal_places"></c-amount.display>
</div>
{% endif %}
{% empty %}
-
{% endfor %}
</td>
{# Month columns #}
{% for month in data.months %}
<td class="text-nowrap">
{% with month_data=item.month_totals %}
{% for m, m_data in month_data.items %}
{% if m == month %}
{% for currency_id, currency_data in m_data.currencies.items %}
<c-amount.display
:amount="currency_data.final_total"
:prefix="currency_data.currency.prefix"
:suffix="currency_data.currency.suffix"
:decimal_places="currency_data.currency.decimal_places"
color="{% if currency_data.final_total < 0 %}red{% elif currency_data.final_total > 0 %}green{% endif %}"></c-amount.display>
{% if currency_data.exchanged %}
<div class="text-xs text-base-content/60">
<c-amount.display
:amount="currency_data.exchanged.final_total"
:prefix="currency_data.exchanged.currency.prefix"
:suffix="currency_data.exchanged.currency.suffix"
:decimal_places="currency_data.exchanged.currency.decimal_places"></c-amount.display>
</div>
{% endif %}
{% empty %}
-
{% endfor %}
{% endif %}
{% empty %}
-
{% endfor %}
{% endwith %}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr class="font-bold bg-base-200">
<th class="sticky left-0 bg-base-200 z-10">{% trans 'Total' %}</th>
{# Grand total #}
<td class="text-nowrap bg-base-300">
{% for currency_id, currency_data in data.grand_total.currencies.items %}
<c-amount.display
:amount="currency_data.final_total"
:prefix="currency_data.currency.prefix"
:suffix="currency_data.currency.suffix"
:decimal_places="currency_data.currency.decimal_places"
color="{% if currency_data.final_total < 0 %}red{% elif currency_data.final_total > 0 %}green{% endif %}"></c-amount.display>
{% if currency_data.exchanged %}
<div class="text-xs text-base-content/60">
<c-amount.display
:amount="currency_data.exchanged.final_total"
:prefix="currency_data.exchanged.currency.prefix"
:suffix="currency_data.exchanged.currency.suffix"
:decimal_places="currency_data.exchanged.currency.decimal_places"></c-amount.display>
</div>
{% endif %}
{% empty %}
-
{% endfor %}
</td>
{# Month totals #}
{% for month in data.months %}
<td class="text-nowrap">
{% with month_total=data.month_totals %}
{% for m, m_data in month_total.items %}
{% if m == month %}
{% for currency_id, currency_data in m_data.currencies.items %}
<c-amount.display
:amount="currency_data.final_total"
:prefix="currency_data.currency.prefix"
:suffix="currency_data.currency.suffix"
:decimal_places="currency_data.currency.decimal_places"
color="{% if currency_data.final_total < 0 %}red{% elif currency_data.final_total > 0 %}green{% endif %}"></c-amount.display>
{% if currency_data.exchanged %}
<div class="text-xs text-base-content/60">
<c-amount.display
:amount="currency_data.exchanged.final_total"
:prefix="currency_data.exchanged.currency.prefix"
:suffix="currency_data.exchanged.currency.suffix"
:decimal_places="currency_data.exchanged.currency.decimal_places"></c-amount.display>
</div>
{% endif %}
{% empty %}
-
{% endfor %}
{% endif %}
{% empty %}
-
{% endfor %}
{% endwith %}
</td>
{% endfor %}
</tr>
</tfoot>
</table>
</div>
</div>
</div>
{% else %}
<c-msg.empty title="{% translate 'No transactions for this year' %}"></c-msg.empty>
{% endif %}
</div>

View File

@@ -0,0 +1,204 @@
{% load i18n %}
<div hx-get="{% url 'insights_year_by_year' %}" hx-trigger="updated from:window" class="show-loading"
hx-swap="outerHTML" hx-include="#group-by-selector">
<div class="h-full text-center mb-4">
<div class="tabs tabs-box mx-auto w-fit" role="group" id="group-by-selector" _="on change trigger updated">
<label class="tab">
<input type="radio"
name="group_by"
id="categories-view"
autocomplete="off"
value="categories"
aria-label="{% trans 'Categories' %}"
{% if group_by == "categories" %}checked{% endif %}>
<i class="fa-solid fa-icons fa-fw me-2"></i>
{% trans 'Categories' %}
</label>
<label class="tab">
<input type="radio"
name="group_by"
id="tags-view"
autocomplete="off"
value="tags"
aria-label="{% trans 'Tags' %}"
{% if group_by == "tags" %}checked{% endif %}>
<i class="fa-solid fa-hashtag fa-fw me-2"></i>
{% trans 'Tags' %}
</label>
<label class="tab">
<input type="radio"
name="group_by"
id="entities-view"
autocomplete="off"
value="entities"
aria-label="{% trans 'Entities' %}"
{% if group_by == "entities" %}checked{% endif %}>
<i class="fa-solid fa-user-group fa-fw me-2"></i>
{% trans 'Entities' %}
</label>
</div>
</div>
{% if data.years %}
<div class="card bg-base-100 card-border">
<div class="card-body">
<div class="overflow-x-auto">
<table class="table">
<thead>
<tr>
<th scope="col" class="sticky left-0 bg-base-100 z-10">
{% if group_by == "categories" %}
{% trans 'Category' %}
{% elif group_by == "tags" %}
{% trans 'Tag' %}
{% else %}
{% trans 'Entity' %}
{% endif %}
</th>
<th scope="col" class="font-bold">{% trans 'Total' %}</th>
{% for year in data.years %}
<th scope="col">{{ year }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for item_id, item in data.items.items %}
<tr>
<th class="text-nowrap sticky left-0 bg-base-100 z-10">
{% if item.name %}
{{ item.name }}
{% else %}
{% if group_by == "categories" %}
{% trans 'Uncategorized' %}
{% elif group_by == "tags" %}
{% trans 'Untagged' %}
{% else %}
{% trans 'No entity' %}
{% endif %}
{% endif %}
</th>
{# Total column for this item #}
<td class="text-nowrap font-semibold bg-base-200">
{% for currency_id, currency_data in item.total.currencies.items %}
<c-amount.display
:amount="currency_data.final_total"
:prefix="currency_data.currency.prefix"
:suffix="currency_data.currency.suffix"
:decimal_places="currency_data.currency.decimal_places"
color="{% if currency_data.final_total < 0 %}red{% elif currency_data.final_total > 0 %}green{% endif %}"></c-amount.display>
{% if currency_data.exchanged %}
<div class="text-xs text-base-content/60">
<c-amount.display
:amount="currency_data.exchanged.final_total"
:prefix="currency_data.exchanged.currency.prefix"
:suffix="currency_data.exchanged.currency.suffix"
:decimal_places="currency_data.exchanged.currency.decimal_places"></c-amount.display>
</div>
{% endif %}
{% empty %}
-
{% endfor %}
</td>
{# Year columns #}
{% for year in data.years %}
<td class="text-nowrap">
{% with year_data=item.year_totals %}
{% for y, y_data in year_data.items %}
{% if y == year %}
{% for currency_id, currency_data in y_data.currencies.items %}
<c-amount.display
:amount="currency_data.final_total"
:prefix="currency_data.currency.prefix"
:suffix="currency_data.currency.suffix"
:decimal_places="currency_data.currency.decimal_places"
color="{% if currency_data.final_total < 0 %}red{% elif currency_data.final_total > 0 %}green{% endif %}"></c-amount.display>
{% if currency_data.exchanged %}
<div class="text-xs text-base-content/60">
<c-amount.display
:amount="currency_data.exchanged.final_total"
:prefix="currency_data.exchanged.currency.prefix"
:suffix="currency_data.exchanged.currency.suffix"
:decimal_places="currency_data.exchanged.currency.decimal_places"></c-amount.display>
</div>
{% endif %}
{% empty %}
-
{% endfor %}
{% endif %}
{% empty %}
-
{% endfor %}
{% endwith %}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr class="font-bold bg-base-200">
<th class="sticky left-0 bg-base-200 z-10">{% trans 'Total' %}</th>
{# Grand total #}
<td class="text-nowrap bg-base-300">
{% for currency_id, currency_data in data.grand_total.currencies.items %}
<c-amount.display
:amount="currency_data.final_total"
:prefix="currency_data.currency.prefix"
:suffix="currency_data.currency.suffix"
:decimal_places="currency_data.currency.decimal_places"
color="{% if currency_data.final_total < 0 %}red{% elif currency_data.final_total > 0 %}green{% endif %}"></c-amount.display>
{% if currency_data.exchanged %}
<div class="text-xs text-base-content/60">
<c-amount.display
:amount="currency_data.exchanged.final_total"
:prefix="currency_data.exchanged.currency.prefix"
:suffix="currency_data.exchanged.currency.suffix"
:decimal_places="currency_data.exchanged.currency.decimal_places"></c-amount.display>
</div>
{% endif %}
{% empty %}
-
{% endfor %}
</td>
{# Year totals #}
{% for year in data.years %}
<td class="text-nowrap">
{% with year_total=data.year_totals %}
{% for y, y_data in year_total.items %}
{% if y == year %}
{% for currency_id, currency_data in y_data.currencies.items %}
<c-amount.display
:amount="currency_data.final_total"
:prefix="currency_data.currency.prefix"
:suffix="currency_data.currency.suffix"
:decimal_places="currency_data.currency.decimal_places"
color="{% if currency_data.final_total < 0 %}red{% elif currency_data.final_total > 0 %}green{% endif %}"></c-amount.display>
{% if currency_data.exchanged %}
<div class="text-xs text-base-content/60">
<c-amount.display
:amount="currency_data.exchanged.final_total"
:prefix="currency_data.exchanged.currency.prefix"
:suffix="currency_data.exchanged.currency.suffix"
:decimal_places="currency_data.exchanged.currency.decimal_places"></c-amount.display>
</div>
{% endif %}
{% empty %}
-
{% endfor %}
{% endif %}
{% empty %}
-
{% endfor %}
{% endwith %}
</td>
{% endfor %}
</tr>
</tfoot>
</table>
</div>
</div>
</div>
{% else %}
<c-msg.empty title="{% translate 'No transactions' %}"></c-msg.empty>
{% endif %}
</div>

View File

@@ -121,6 +121,16 @@
hx-get="{% url 'insights_emergency_fund' %}">
{% trans 'Emergency Fund' %}
</button>
<button class="btn btn-ghost btn-free justify-start text-start" data-bs-target="#v-pills-content"
type="button" role="tab" aria-controls="v-pills-content" aria-selected="false"
hx-get="{% url 'insights_year_by_year' %}">
{% trans 'Year by Year' %}
</button>
<button class="btn btn-ghost btn-free justify-start text-start" data-bs-target="#v-pills-content"
type="button" role="tab" aria-controls="v-pills-content" aria-selected="false"
hx-get="{% url 'insights_month_by_month' %}">
{% trans 'Month by Month' %}
</button>
</div>
</div>
</div>

View File

@@ -3,6 +3,31 @@
{% regroup transactions by date|customnaturaldate as transactions_by_date %}
<div id="transactions-list">
{% if late_transactions %}
<div id="late-transactions" class="transactions-divider"
x-data="{ open: sessionStorage.getItem('late-transactions') !== 'false' }"
x-init="if (sessionStorage.getItem('late-transactions') === null) sessionStorage.setItem('late-transactions', 'true')">
<div class="mt-3 mb-1 w-full border-b border-b-error/50 transactions-divider-title cursor-pointer">
<a class="no-underline inline-block w-full text-error font-semibold"
role="button"
@click="open = !open; sessionStorage.setItem('late-transactions', open)"
:aria-expanded="open">
<i class="fa-solid fa-circle-exclamation me-1"></i>{% translate "late" %}
</a>
</div>
<div class="transactions-divider-collapse overflow-visible isolation-auto"
x-show="open"
x-collapse>
<div class="flex flex-col">
{% for transaction in late_transactions %}
<c-transaction.item
:transaction="transaction"></c-transaction.item>
{% endfor %}
</div>
</div>
</div>
{% endif %}
{% for x in transactions_by_date %}
<div id="{{ x.grouper|slugify }}" class="transactions-divider"
x-data="{ open: sessionStorage.getItem('{{ x.grouper|slugify }}') !== 'false' }"
@@ -28,10 +53,13 @@
</div>
{% empty %}
{% if not late_transactions %}
<c-msg.empty
title="{% translate 'No transactions this month' %}"
subtitle="{% translate "Try adding one" %}"></c-msg.empty>
{% endif %}
{% endfor %}
{# Floating bar #}
<c-ui.transactions-action-bar></c-ui.transactions-action-bar>
</div>

View File

@@ -1,6 +1,7 @@
{% load i18n %}
{% load currency_display %}
<div class="grid grid-cols-1 gap-4 mt-1 mb-3">
{% if not has_active_filter %}
{# Daily Spending#}
<div>
<c-ui.info-card color="yellow" icon="fa-solid fa-calendar-day" title="{% trans 'Daily Spending Allowance' %}" help_text={% trans "This is the final total divided by the remaining days in the month" %}>
@@ -34,6 +35,7 @@
</div>
</c-ui.info-card>
</div>
{% endif %}
{# Income#}
<div>
<c-ui.info-card color="green" icon="fa-solid fa-arrow-right-to-bracket" title="{% trans 'Income' %}">

View File

@@ -50,12 +50,13 @@
role="tab"
{% if summary_tab == 'summary' or not summary_tab %}checked="checked"{% endif %}
_="on click fetch {% url 'monthly_summary_select' selected='summary' %}"
aria-controls="summary-tab-pane" />
aria-controls="summary-tab-pane"/>
<div class="tab-content" id="summary-tab-pane" role="tabpanel">
<div id="summary"
hx-get="{% url 'monthly_summary' month=month year=year %}"
class="show-loading"
hx-trigger="load, updated from:window, selective_update from:window, every 10m">
hx-trigger="load, updated from:window, selective_update from:window, every 10m"
hx-include="#filter">
</div>
</div>
@@ -68,7 +69,8 @@
<div id="currency-summary"
hx-get="{% url 'monthly_currency_summary' month=month year=year %}"
class="show-loading"
hx-trigger="load, updated from:window, selective_update from:window, every 10m">
hx-trigger="load, updated from:window, selective_update from:window, every 10m"
hx-include="#filter">
</div>
</div>
@@ -81,7 +83,8 @@
<div id="account-summary"
hx-get="{% url 'monthly_account_summary' month=month year=year %}"
class="show-loading"
hx-trigger="load, updated from:window, selective_update from:window, every 10m">
hx-trigger="load, updated from:window, selective_update from:window, every 10m"
hx-include="#filter">
</div>
</div>
</div>
@@ -100,11 +103,112 @@
{# Main control bar with filter, search, and ordering #}
<div class="join w-full">
<button class="btn btn-secondary join-item relative" type="button"
<button class="btn btn-secondary join-item relative z-1" type="button"
@click="filterOpen = !filterOpen"
:aria-expanded="filterOpen" id="filter-button"
title="{% translate 'Filter transactions' %}">
title="{% translate 'Filter transactions' %}"
_="on load or change from #filter
-- Check if any filter has a non-default value
set hasActiveFilter to false
-- Check type (default is both IN and EX checked)
set typeInputs to <input[name='type']:checked/> in #filter
if typeInputs.length is not 2
set hasActiveFilter to true
end
-- Check is_paid (default is both 1 and 0 checked)
set isPaidInputs to <input[name='is_paid']:checked/> in #filter
if isPaidInputs.length is not 2
set hasActiveFilter to true
end
-- Check mute_status (default is both active and muted checked)
set muteStatusInputs to <input[name='mute_status']:checked/> in #filter
if muteStatusInputs.length is not 2
set hasActiveFilter to true
end
-- Check description
set descInput to #id_description
if descInput exists and descInput.value is not ''
set hasActiveFilter to true
end
-- Check date_start
set dateStartInput to #id_date_start
if dateStartInput exists and dateStartInput.value is not ''
set hasActiveFilter to true
end
-- Check date_end
set dateEndInput to #id_date_end
if dateEndInput exists and dateEndInput.value is not ''
set hasActiveFilter to true
end
-- Check reference_date_start
set refDateStartInput to #id_reference_date_start
if refDateStartInput exists and refDateStartInput.value is not ''
set hasActiveFilter to true
end
-- Check reference_date_end
set refDateEndInput to #id_reference_date_end
if refDateEndInput exists and refDateEndInput.value is not ''
set hasActiveFilter to true
end
-- Check from_amount
set fromAmountInput to #id_from_amount
if fromAmountInput exists and fromAmountInput.value is not ''
set hasActiveFilter to true
end
-- Check to_amount
set toAmountInput to #id_to_amount
if toAmountInput exists and toAmountInput.value is not ''
set hasActiveFilter to true
end
-- Check account (TomSelect stores values differently)
set accountInput to #id_account
if accountInput exists and accountInput.value is not ''
set hasActiveFilter to true
end
-- Check currency
set currencyInput to #id_currency
if currencyInput exists and currencyInput.value is not ''
set hasActiveFilter to true
end
-- Check category
set categoryInput to #id_category
if categoryInput exists and categoryInput.value is not ''
set hasActiveFilter to true
end
-- Check tags
set tagsInput to #id_tags
if tagsInput exists and tagsInput.value is not ''
set hasActiveFilter to true
end
-- Check entities
set entitiesInput to #id_entities
if entitiesInput exists and entitiesInput.value is not ''
set hasActiveFilter to true
end
-- Show or hide the indicator
if hasActiveFilter
remove .hidden from #filter-active-indicator
else
add .hidden to #filter-active-indicator
end">
<i class="fa-solid fa-filter fa-fw"></i>
<span id="filter-active-indicator" class="absolute -top-1 -right-1 w-3 h-3 bg-error rounded-full hidden z-10"></span>
</button>
{# Search box #}

View File

@@ -3,6 +3,31 @@
{% regroup page_obj by date|customnaturaldate as transactions_by_date %}
<div id="transactions-list" class="show-loading">
{% if late_transactions %}
<div id="late-transactions" class="transactions-divider"
x-data="{ open: sessionStorage.getItem('late-transactions') !== 'false' }"
x-init="if (sessionStorage.getItem('late-transactions') === null) sessionStorage.setItem('late-transactions', 'true')">
<div class="mt-3 mb-1 w-full border-b border-b-error/50 transactions-divider-title cursor-pointer">
<a class="no-underline inline-block w-full text-error font-semibold"
role="button"
@click="open = !open; sessionStorage.setItem('late-transactions', open)"
:aria-expanded="open">
<i class="fa-solid fa-circle-exclamation me-1"></i>{% translate "late" %}
</a>
</div>
<div class="transactions-divider-collapse overflow-visible isolation-auto"
x-show="open"
x-collapse>
<div class="flex flex-col">
{% for transaction in late_transactions %}
<c-transaction.item
:transaction="transaction"></c-transaction.item>
{% endfor %}
</div>
</div>
</div>
{% endif %}
{% for x in transactions_by_date %}
<div id="{{ x.grouper|slugify }}" class="transactions-divider"
x-data="{ open: sessionStorage.getItem('{{ x.grouper|slugify }}') !== 'false' }"
@@ -28,9 +53,11 @@
</div>
{% empty %}
{% if not late_transactions %}
<c-msg.empty
title="{% translate "No transactions found" %}"
subtitle="{% translate "Try adding one" %}"></c-msg.empty>
{% endif %}
{% endfor %}
{# Floating bar #}

View File

@@ -52,11 +52,112 @@
{# Main control bar with filter, search, and ordering #}
<div class="join w-full">
<button class="btn btn-secondary join-item relative" type="button"
<button class="btn btn-secondary join-item relative z-1" type="button"
@click="filterOpen = !filterOpen"
:aria-expanded="filterOpen" id="filter-button"
title="{% translate 'Filter transactions' %}">
title="{% translate 'Filter transactions' %}"
_="on load or change from #filter
-- Check if any filter has a non-default value
set hasActiveFilter to false
-- Check type (default is both IN and EX checked)
set typeInputs to <input[name='type']:checked/> in #filter
if typeInputs.length is not 2
set hasActiveFilter to true
end
-- Check is_paid (default is both 1 and 0 checked)
set isPaidInputs to <input[name='is_paid']:checked/> in #filter
if isPaidInputs.length is not 2
set hasActiveFilter to true
end
-- Check mute_status (default is both active and muted checked)
set muteStatusInputs to <input[name='mute_status']:checked/> in #filter
if muteStatusInputs.length is not 2
set hasActiveFilter to true
end
-- Check description
set descInput to #id_description
if descInput exists and descInput.value is not ''
set hasActiveFilter to true
end
-- Check date_start
set dateStartInput to #id_date_start
if dateStartInput exists and dateStartInput.value is not ''
set hasActiveFilter to true
end
-- Check date_end
set dateEndInput to #id_date_end
if dateEndInput exists and dateEndInput.value is not ''
set hasActiveFilter to true
end
-- Check reference_date_start
set refDateStartInput to #id_reference_date_start
if refDateStartInput exists and refDateStartInput.value is not ''
set hasActiveFilter to true
end
-- Check reference_date_end
set refDateEndInput to #id_reference_date_end
if refDateEndInput exists and refDateEndInput.value is not ''
set hasActiveFilter to true
end
-- Check from_amount
set fromAmountInput to #id_from_amount
if fromAmountInput exists and fromAmountInput.value is not ''
set hasActiveFilter to true
end
-- Check to_amount
set toAmountInput to #id_to_amount
if toAmountInput exists and toAmountInput.value is not ''
set hasActiveFilter to true
end
-- Check account (TomSelect stores values differently)
set accountInput to #id_account
if accountInput exists and accountInput.value is not ''
set hasActiveFilter to true
end
-- Check currency
set currencyInput to #id_currency
if currencyInput exists and currencyInput.value is not ''
set hasActiveFilter to true
end
-- Check category
set categoryInput to #id_category
if categoryInput exists and categoryInput.value is not ''
set hasActiveFilter to true
end
-- Check tags
set tagsInput to #id_tags
if tagsInput exists and tagsInput.value is not ''
set hasActiveFilter to true
end
-- Check entities
set entitiesInput to #id_entities
if entitiesInput exists and entitiesInput.value is not ''
set hasActiveFilter to true
end
-- Show or hide the indicator
if hasActiveFilter
remove .hidden from #filter-active-indicator
else
add .hidden to #filter-active-indicator
end">
<i class="fa-solid fa-filter fa-fw"></i>
<span id="filter-active-indicator" class="absolute -top-1 -right-1 w-3 h-3 bg-error rounded-full hidden z-10"></span>
</button>
{# Search box #}

View File

@@ -2,6 +2,7 @@ volumes:
wygiwyh_dev_postgres_data: {}
wygiwyh_temp:
services:
web:
build:

View File

@@ -1,14 +1,4 @@
FROM python:3.11-slim-bookworm AS python-build-stage
RUN apt-get update && apt-get install --no-install-recommends -y \
build-essential \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
COPY ../requirements.txt .
RUN pip wheel --wheel-dir /usr/src/app/wheels -r requirements.txt
FROM python:3.11-slim-bookworm AS python-run-stage
FROM ghcr.io/astral-sh/uv:python3.11-bookworm-slim
ARG VERSION=dev
ENV APP_VERSION=$VERSION
@@ -16,16 +6,17 @@ ENV APP_VERSION=$VERSION
WORKDIR /usr/src/app
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
COPY --from=python-build-stage /usr/src/app/wheels /wheels/
PYTHONUNBUFFERED=1 \
UV_COMPILE_BYTECODE=1 \
UV_LINK_MODE=copy \
UV_PROJECT_ENVIRONMENT=/opt/venv
RUN apt-get update && \
apt-get install --no-install-recommends -y gettext supervisor && \
rm -rf /var/lib/apt/lists/* && \
pip install --upgrade pip && \
pip install --no-cache-dir --no-index --find-links=/wheels/ /wheels/* && \
rm -rf /wheels/ ~/.cache/pip/*
rm -rf /var/lib/apt/lists/*
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-install-project --no-dev
COPY ./docker/dev/django/start /start
COPY ./docker/dev/procrastinate/start /start-procrastinate
@@ -40,6 +31,8 @@ RUN sed -i 's/\r$//g' /start && \
sed -i 's/\r$//g' /start-supervisor && \
chmod +x /start-supervisor
ENV PATH="/opt/venv/bin:$PATH"
COPY ./app .
CMD ["/start-supervisor"]

View File

@@ -1,17 +1,19 @@
FROM python:3.11-slim-bookworm AS python-build-stage
FROM ghcr.io/astral-sh/uv:python3.11-bookworm-slim AS python-build-stage
WORKDIR /app
ENV UV_COMPILE_BYTECODE=1 \
UV_LINK_MODE=copy
RUN apt-get update && apt-get install --no-install-recommends -y \
build-essential \
libpq-dev \
curl \
&& rm -rf /var/lib/apt/lists/*
# Install uv for faster package resolution
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
COPY uv.lock pyproject.toml ./
COPY ./requirements.txt .
RUN --mount=type=cache,target=/root/.cache/uv \
uv pip install --system --compile-bytecode -r requirements.txt
uv sync --frozen --no-install-project --no-dev
FROM node:lts-alpine AS vite_build
WORKDIR /usr/src/frontend
@@ -33,9 +35,8 @@ ENV APP_VERSION=$VERSION
COPY --from=vite_build /usr/src/frontend/build /usr/src/frontend/build
# Copy Python packages from build stage
COPY --from=python-build-stage /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=python-build-stage /usr/local/bin /usr/local/bin
# Copy virtual environment from build stage
COPY --from=python-build-stage /app/.venv /app/.venv
WORKDIR /usr/src/app
@@ -43,7 +44,8 @@ RUN addgroup --system app && \
adduser --system --ingroup app app
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
PYTHONUNBUFFERED=1 \
PATH="/app/.venv/bin:$PATH"
# Install runtime dependencies
RUN --mount=type=cache,target=/root/.cache/apt \

40
pyproject.toml Normal file
View File

@@ -0,0 +1,40 @@
[project]
name = "wygiwyh"
dynamic = ["version"]
description = "An opinionated and powerful finance tracker."
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"crispy-bootstrap5==2025.6",
"django~=5.2.9",
"django-allauth[socialaccount]~=65.13.1",
"django-browser-reload==1.21.0",
"django-cachalot~=2.8.0",
"django-cotton<2.3.0",
"django-crispy-forms==2.5",
"django-debug-toolbar==6.1.0",
"django-filter==25.2",
"django-hijack==3.7.4",
"django-import-export~=4.3.9",
"django-pwa~=2.0.1",
"django-vite==3.1.0",
"djangorestframework~=3.16.0",
"drf-spectacular~=0.29.0",
"gunicorn==23.0.0",
"mistune~=3.1.3",
"openpyxl~=3.1.5",
"procrastinate[django]~=3.5.3",
"psycopg[binary,pool]==3.2.9",
"pydantic~=2.12.3",
"python-dateutil~=2.9.0.post0",
"pytz>=2025.2",
"pyyaml~=6.0.2",
"requests~=2.32.5",
"simpleeval~=1.0.3",
"watchfiles==1.1.1",
"whitenoise[brotli]==6.11.0",
"xlrd~=2.0.1",
]
[tool.setuptools]
packages = ["app"]

View File

@@ -1,33 +0,0 @@
Django~=5.2.9
psycopg[binary,pool]==3.2.9
django-vite==3.1.0
django-crispy-forms==2.5
crispy-bootstrap5==2025.6
django-browser-reload==1.21.0
django-hijack==3.7.4
django-filter==25.2
django-debug-toolbar==6.1.0
django-cachalot~=2.8.0
django-cotton<2.3.0
django-pwa~=2.0.1
djangorestframework~=3.16.0
drf-spectacular~=0.29.0
django-import-export~=4.3.9
gunicorn==23.0.0
whitenoise[brotli]==6.11.0
watchfiles==1.1.1
procrastinate[django]~=3.5.3
requests~=2.32.5
django-allauth[socialaccount]~=65.13.1
pytz
python-dateutil~=2.9.0.post0
simpleeval~=1.0.3
pydantic~=2.12.3
PyYAML~=6.0.2
mistune~=3.1.3
openpyxl~=3.1.5
xlrd~=2.0.1

1337
uv.lock generated Normal file

File diff suppressed because it is too large Load Diff