Compare commits

..

49 Commits
0.7.4 ... 0.8.0

Author SHA1 Message Date
Herculino Trotta
448841dadc Merge pull request #104 from eitchtee/dev
fix: wrong filename
2025-01-29 00:15:32 -03:00
Herculino Trotta
1b6934694e fix: wrong filename 2025-01-29 00:14:45 -03:00
Herculino Trotta
d4d00ba02f Merge pull request #103 from eitchtee/dev
feat: reduce db queries when saving order on session
2025-01-29 00:14:18 -03:00
Herculino Trotta
19a65ac45f feat: reduce db queries when saving order on session 2025-01-29 00:12:47 -03:00
Herculino Trotta
b72e7bd707 Merge pull request #102
docker: set single container as new default
2025-01-29 00:12:40 -03:00
Herculino Trotta
190be3e813 docker: set single container as new default 2025-01-29 00:11:39 -03:00
Herculino Trotta
88300b314c Merge pull request #101 from eitchtee/eitchtee-patch-1
Update release.yml
2025-01-28 23:47:34 -03:00
Herculino Trotta
fab77c8d9f Update release.yml 2025-01-28 23:47:18 -03:00
Herculino Trotta
1ae7158d7e Merge pull request #100 from eitchtee/dev
docker: fix permission error
2025-01-28 23:46:11 -03:00
Herculino Trotta
05f0356288 docker: fix permission error 2025-01-28 23:45:01 -03:00
Herculino Trotta
b3cea17b8d Merge pull request #99
docker: add single-container support
2025-01-28 23:35:08 -03:00
Herculino Trotta
0b66b23f16 docker: add single-container support 2025-01-28 23:34:48 -03:00
Herculino Trotta
80fdf70f7d Add a nightly docker tag built whenever there's a push to main 2025-01-28 23:13:23 -03:00
Herculino Trotta
fa931b0db2 Merge pull request #98
feat: cleanup expired sessions every first day of month at 6am
2025-01-28 21:33:00 -03:00
Herculino Trotta
cab79b4203 feat: cleanup expired sessions every first day of month at 6am 2025-01-28 21:32:41 -03:00
Herculino Trotta
ddab3db6b5 Merge pull request #97
feat(import:v1): accept list as source, first valid one will be used.
2025-01-28 21:24:44 -03:00
Herculino Trotta
9fa704811c feat(import:v1): accept list as source, first valid one will be used. 2025-01-28 21:24:23 -03:00
Herculino Trotta
4c0d14def0 Merge pull request #96
feat: store selected "order by" on session
2025-01-28 20:05:46 -03:00
Herculino Trotta
43382d2ffe feat: store selected "order by" on session
Closes #95
2025-01-28 20:05:00 -03:00
Herculino Trotta
27d448afd6 feat: add locale files for de (german) 2025-01-28 14:03:38 -03:00
Herculino Trotta
1dd90974bd Merge pull request #93
refactor: remove toasts from login screen
2025-01-28 13:54:20 -03:00
Herculino Trotta
31cc8db3ac refactor: remove toasts from login screen
Fixes #91
2025-01-28 13:53:47 -03:00
Herculino Trotta
3d85a15ec9 Merge pull request #90
feat: enable bulk actions on specific transactions list (calendar, recurring and installment)
2025-01-27 22:46:19 -03:00
Herculino Trotta
90f98c2d15 feat: enable bulk actions on specific transactions list (calendar, recurring and installment) 2025-01-27 22:45:40 -03:00
Herculino Trotta
643855e60e Merge pull request #89 from eitchtee/dev
fix(calendar): tooltip error when transaction has no description and wrong color
2025-01-27 22:44:43 -03:00
Herculino Trotta
e0f7b532f8 fix(calendar): tooltip error when transaction has no description and wrong color 2025-01-27 22:44:05 -03:00
Herculino Trotta
b4d3e4b42f Merge pull request #88 from eitchtee/dev
feat: add "Clear cache" button to user menu
2025-01-27 21:50:48 -03:00
Herculino Trotta
9a7ccb0973 feat: add "Clear cache" button to user menu 2025-01-27 21:49:32 -03:00
Herculino Trotta
a9b67ff272 Merge pull request #87
fix(security): toasts and month_year_picker accessible without login
2025-01-27 21:42:36 -03:00
Herculino Trotta
233b9629a2 fix(security): toasts and month_year_picker accessible without login 2025-01-27 21:41:55 -03:00
Herculino Trotta
4180c177f1 Merge pull request #86
fix: cleanup_deleted_transactions task couldn't trigger
2025-01-27 21:34:15 -03:00
Herculino Trotta
f1bc04756f fix: cleanup_deleted_transactions task couldn't trigger 2025-01-27 21:33:46 -03:00
Herculino Trotta
13795c797f Merge pull request #85
feat: add number format user setting and improve date format handling
2025-01-27 13:31:28 -03:00
Herculino Trotta
331a7d5b18 locale: update translations 2025-01-27 13:30:06 -03:00
Herculino Trotta
81b8da30d6 feat: add number_format to user_settings form 2025-01-27 13:26:08 -03:00
Herculino Trotta
80bad240e7 refactor: remove custom_date filter 2025-01-27 13:25:47 -03:00
Herculino Trotta
187c56c96c refactor: remove user attr from datepicker
since monkey patched get_format already does what we want
2025-01-27 13:25:06 -03:00
Herculino Trotta
3796112d77 feat: monkey patch get_format to return usersettings 2025-01-27 13:22:21 -03:00
Herculino Trotta
958940089a feat: add number_format user setting 2025-01-27 13:20:12 -03:00
Herculino Trotta
a08548bb13 feat: add local access to user and request from anywhere 2025-01-27 13:19:28 -03:00
Herculino Trotta
7fe446e510 refactor: remove custom_date filter 2025-01-27 13:18:57 -03:00
Herculino Trotta
eccb0d15ee Merge pull request #83 from eitchtee/eitchtee-patch-1
Update README.md
2025-01-26 21:03:45 -03:00
Herculino Trotta
7ebd329706 Update README.md 2025-01-26 21:03:14 -03:00
Herculino Trotta
d3fcd5fe7e Merge pull request #82
fix datepicker datetime handling and action-bar
2025-01-26 20:56:53 -03:00
Herculino Trotta
b0a3acbdde fix: transactions action bar error on page change 2025-01-26 20:56:03 -03:00
Herculino Trotta
33ce38d74c feat(datepicker): improve value handling 2025-01-26 20:54:29 -03:00
Herculino Trotta
fa51a7fef9 fix(datepicker): wrong datetime format 2025-01-26 20:53:16 -03:00
Herculino Trotta
d7c072a35c fix(currencies): don't error out if from_currency or to_currency isn't set 2025-01-26 20:52:47 -03:00
Herculino Trotta
c88a6dcf3a Update README.md 2025-01-26 11:49:28 -03:00
57 changed files with 3102 additions and 488 deletions

View File

@@ -23,3 +23,5 @@ WEB_CONCURRENCY=4
ENABLE_SOFT_DELETE=false ENABLE_SOFT_DELETE=false
# If ENABLE_SOFT_DELETE is true, transactions deleted for more than KEEP_DELETED_TRANSACTIONS_FOR days will be truly deleted. Set to 0 to keep all. # If ENABLE_SOFT_DELETE is true, transactions deleted for more than KEEP_DELETED_TRANSACTIONS_FOR days will be truly deleted. Set to 0 to keep all.
KEEP_DELETED_TRANSACTIONS_FOR=365 KEEP_DELETED_TRANSACTIONS_FOR=365
TASK_WORKERS=1 # This only work if you're using the single container option. Increase to have more open queues via procrastinate, you probably don't need to increase this.

View File

@@ -3,6 +3,8 @@ name: Release Pipeline
on: on:
release: release:
types: [created] types: [created]
push:
branches: [ main ]
env: env:
IMAGE_NAME: wygiwyh IMAGE_NAME: wygiwyh
@@ -29,7 +31,21 @@ jobs:
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
- name: Build and push image - name: Build and push nightly image
if: github.event_name == 'push'
uses: docker/build-push-action@v6
with:
context: .
file: ./docker/prod/django/Dockerfile
push: true
provenance: false
tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:nightly
platforms: linux/amd64,linux/arm64
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Build and push release image
if: github.event_name == 'release'
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
context: . context: .

View File

@@ -95,31 +95,16 @@ You can now access localhost:OUTBOUND_PORT
> [!NOTE] > [!NOTE]
> If you're going to use another IP that isn't localhost, add it to `DJANGO_ALLOWED_HOSTS`, without `http://` > If you're going to use another IP that isn't localhost, add it to `DJANGO_ALLOWED_HOSTS`, without `http://`
## Building from source ## Building from source
Features are only added to main when ready, if you want to run the latest version, you must build from source.
Features are only added to `main` when ready, if you want to run the latest version, you must build from source. All the required Dockerfiles are [here](https://github.com/eitchtee/WYGIWYH/tree/main/docker/prod).
```bash ## Unraid
# Create a folder for WYGIWYH (optional)
$ mkdir WYGIWYH
# Go into the folder [nwithan8](https://github.com/nwithan8) has kindly provided a Unraid template for WYGIWYH, have a look at the [unraid_templates](https://github.com/nwithan8/unraid_templates) repo.
$ cd WYGIWYH
# Clone this repository WYGIWYH and WYGIWYH--Procrastinate should be available on the Unraid Store. You need both for all features.
$ git clone https://github.com/eitchtee/WYGIWYH.git .
$ cp docker-compose.prod.yml docker-compose.yml
$ cp .env.example .env
# Now edit both files as you see fit
# Run the app
$ docker compose up -d --build
# Create the first admin account
$ docker compose exec -it web python manage.py createsuperuser
```
# How it works # How it works

View File

@@ -77,6 +77,7 @@ INSTALLED_APPS = [
] ]
MIDDLEWARE = [ MIDDLEWARE = [
"apps.common.middleware.thread_local.ThreadLocalMiddleware",
"debug_toolbar.middleware.DebugToolbarMiddleware", "debug_toolbar.middleware.DebugToolbarMiddleware",
"django.middleware.security.SecurityMiddleware", "django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware", "whitenoise.middleware.WhiteNoiseMiddleware",
@@ -221,7 +222,7 @@ SESSION_COOKIE_SECURE = os.getenv("HTTPS_ENABLED", "false").lower() == "true"
DEBUG_TOOLBAR_CONFIG = { DEBUG_TOOLBAR_CONFIG = {
"ROOT_TAG_EXTRA_ATTRS": "hx-preserve", "ROOT_TAG_EXTRA_ATTRS": "hx-preserve",
"SHOW_TOOLBAR_CALLBACK": lambda r: False, # disables it} # "SHOW_TOOLBAR_CALLBACK": lambda r: False, # disables it
} }
DEBUG_TOOLBAR_PANELS = [ DEBUG_TOOLBAR_PANELS = [
"debug_toolbar.panels.history.HistoryPanel", "debug_toolbar.panels.history.HistoryPanel",

View File

@@ -0,0 +1,31 @@
from apps.common.middleware.thread_local import get_current_user
from django.utils.formats import get_format as original_get_format
def get_format(format_type=None, lang=None, use_l10n=None):
user = get_current_user()
if user and user.is_authenticated and hasattr(user, "settings"):
user_settings = user.settings
if format_type == "THOUSAND_SEPARATOR":
number_format = getattr(user_settings, "number_format", None)
if number_format == "DC":
return "."
elif number_format == "CD":
return ","
elif format_type == "DECIMAL_SEPARATOR":
number_format = getattr(user_settings, "number_format", None)
if number_format == "DC":
return ","
elif number_format == "CD":
return "."
elif format_type == "SHORT_DATE_FORMAT":
date_format = getattr(user_settings, "date_format", None)
if date_format and date_format != "SHORT_DATE_FORMAT":
return date_format
elif format_type == "SHORT_DATETIME_FORMAT":
datetime_format = getattr(user_settings, "datetime_format", None)
if datetime_format and datetime_format != "SHORT_DATETIME_FORMAT":
return datetime_format
return original_get_format(format_type, lang, use_l10n)

View File

@@ -1,14 +1,17 @@
import zoneinfo import zoneinfo
from django.utils import formats
from django.utils import timezone, translation from django.utils import timezone, translation
from django.utils.translation import activate from django.utils.functional import lazy
from apps.common.functions.format import get_format as custom_get_format
from apps.users.models import UserSettings from apps.users.models import UserSettings
class LocalizationMiddleware: class LocalizationMiddleware:
def __init__(self, get_response): def __init__(self, get_response):
self.get_response = get_response self.get_response = get_response
self.patch_get_format()
def __call__(self, request): def __call__(self, request):
tz = request.COOKIES.get("mytz") tz = request.COOKIES.get("mytz")
@@ -33,9 +36,14 @@ class LocalizationMiddleware:
timezone.activate(zoneinfo.ZoneInfo("UTC")) timezone.activate(zoneinfo.ZoneInfo("UTC"))
if user_language and user_language != "auto": if user_language and user_language != "auto":
activate(user_language) translation.activate(user_language)
else: else:
detected_language = translation.get_language_from_request(request) detected_language = translation.get_language_from_request(request)
activate(detected_language) translation.activate(detected_language)
return self.get_response(request) return self.get_response(request)
@staticmethod
def patch_get_format():
formats.get_format = custom_get_format
formats.get_format_lazy = lazy(custom_get_format, str, list, tuple)

View File

@@ -0,0 +1,73 @@
"""
threadlocals middleware
~~~~~~~~~~~~~~~~~~~~~~~
make the request object everywhere available (e.g. in model instance).
based on: http://code.djangoproject.com/wiki/CookBookThreadlocalsAndUser
Put this into your settings:
--------------------------------------------------------------------------
MIDDLEWARE_CLASSES = (
...
'django_tools.middlewares.ThreadLocal.ThreadLocalMiddleware',
...
)
--------------------------------------------------------------------------
Usage:
--------------------------------------------------------------------------
from django_tools.middlewares import ThreadLocal
# Get the current request object:
request = ThreadLocal.get_current_request()
# You can get the current user directly with:
user = ThreadLocal.get_current_user()
--------------------------------------------------------------------------
:copyleft: 2009-2017 by the django-tools team, see AUTHORS for more details.
:license: GNU GPL v3 or above, see LICENSE for more details.
"""
try:
from threading import local
except ImportError:
from django.utils._threading_local import local
try:
from django.utils.deprecation import MiddlewareMixin
except ImportError:
MiddlewareMixin = object # fallback for Django < 1.10
_thread_locals = local()
def get_current_request():
"""returns the request object for this thread"""
return getattr(_thread_locals, "request", None)
def get_current_user():
"""returns the current user, if exist, otherwise returns None"""
request = get_current_request()
if request:
return getattr(request, "user", None)
class ThreadLocalMiddleware(MiddlewareMixin):
"""Simple middleware that adds the request object in thread local storage."""
def process_request(self, request):
_thread_locals.request = request
def process_response(self, request, response):
if hasattr(_thread_locals, "request"):
del _thread_locals.request
return response
def process_exception(self, request, exception):
if hasattr(_thread_locals, "request"):
del _thread_locals.request

View File

@@ -1,5 +1,8 @@
import logging import logging
from asgiref.sync import sync_to_async
from django.core import management
from procrastinate import builtin_tasks from procrastinate import builtin_tasks
from procrastinate.contrib.django import app from procrastinate.contrib.django import app
@@ -24,3 +27,16 @@ async def remove_old_jobs(context, timestamp):
exc_info=True, exc_info=True,
) )
raise e raise e
@app.periodic(cron="0 6 1 * *")
@app.task(queueing_lock="remove_expired_sessions")
async def remove_expired_sessions(timestamp=None):
"""Cleanup expired sessions by using Django management command."""
try:
await sync_to_async(management.call_command)("clearsessions", verbosity=0)
except Exception:
logger.error(
"Error while executing 'remove_expired_sessions' task",
exc_info=True,
)

View File

@@ -1,32 +0,0 @@
from django import template
from django.template.defaultfilters import date as date_filter
from django.utils import formats, timezone
register = template.Library()
@register.filter
def custom_date(value, user=None):
if not value:
return ""
# Determine if the value is a datetime or just a date
is_datetime = hasattr(value, "hour")
# Convert to current timezone if it's a datetime
if is_datetime and timezone.is_aware(value):
value = timezone.localtime(value)
if user and user.is_authenticated:
user_settings = user.settings
if is_datetime:
format_setting = user_settings.datetime_format
else:
format_setting = user_settings.date_format
return formats.date_format(value, format_setting, use_l10n=True)
return date_filter(
value, "SHORT_DATE_FORMAT" if not is_datetime else "SHORT_DATETIME_FORMAT"
)

View File

@@ -1,6 +1,6 @@
from django import template from django import template
from django.utils.formats import get_format
from apps.common.functions.format import get_format
register = template.Library() register = template.Library()

View File

@@ -13,4 +13,9 @@ urlpatterns = [
views.month_year_picker, views.month_year_picker,
name="month_year_picker", name="month_year_picker",
), ),
path(
"cache/invalidate/",
views.invalidate_cache,
name="invalidate_cache",
),
] ]

View File

@@ -35,7 +35,7 @@ def django_to_python_datetime(django_format):
def django_to_airdatepicker_datetime(django_format): def django_to_airdatepicker_datetime(django_format):
format_map = { format_map = {
# Time # Time
"h": "h", # Hour (12-hour) "h": "hh", # Hour (12-hour)
"H": "H", # Hour (24-hour) "H": "H", # Hour (24-hour)
"i": "m", # Minutes "i": "m", # Minutes
"A": "AA", # AM/PM uppercase "A": "AA", # AM/PM uppercase
@@ -76,7 +76,7 @@ def django_to_airdatepicker_datetime(django_format):
def django_to_airdatepicker_datetime_separated(django_format): def django_to_airdatepicker_datetime_separated(django_format):
format_map = { format_map = {
# Time formats # Time formats
"h": "hH", # Hour (12-hour) "h": "hh", # Hour (12-hour)
"H": "HH", # Hour (24-hour) "H": "HH", # Hour (24-hour)
"i": "mm", # Minutes "i": "mm", # Minutes
"A": "AA", # AM/PM uppercase "A": "AA", # AM/PM uppercase

View File

@@ -1,17 +1,32 @@
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.db.models import Count from django.db.models import Count
from django.db.models.functions import ExtractYear, ExtractMonth from django.db.models.functions import ExtractYear, ExtractMonth
from django.http import HttpResponse
from django.shortcuts import render from django.shortcuts import render
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.views.decorators.http import require_http_methods
from django.utils.translation import gettext_lazy as _
from cachalot.api import invalidate
from apps.common.decorators.htmx import only_htmx
from apps.transactions.models import Transaction from apps.transactions.models import Transaction
@only_htmx
@login_required
@require_http_methods(["GET"])
def toasts(request): def toasts(request):
return render(request, "common/fragments/toasts.html") return render(request, "common/fragments/toasts.html")
@only_htmx
@login_required
@require_http_methods(["GET"])
def month_year_picker(request): def month_year_picker(request):
field = request.GET.get("field", "reference_date") field = request.GET.get("field", "reference_date")
for_ = request.GET.get("for", None) for_ = request.GET.get("for", None)
@@ -84,3 +99,19 @@ def month_year_picker(request):
"current_year": current_year, "current_year": current_year,
}, },
) )
@only_htmx
@login_required
@require_http_methods(["GET"])
def invalidate_cache(request):
invalidate()
messages.success(request, _("Cache cleared successfully"))
return HttpResponse(
status=204,
headers={
"HX-Trigger": "updated",
},
)

View File

@@ -2,7 +2,6 @@ import datetime
from django.forms import widgets from django.forms import widgets
from django.utils import formats, translation, dates from django.utils import formats, translation, dates
from django.utils.formats import get_format
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from apps.common.utils.django import ( from apps.common.utils.django import (
@@ -10,6 +9,7 @@ from apps.common.utils.django import (
django_to_airdatepicker_datetime, django_to_airdatepicker_datetime,
django_to_airdatepicker_datetime_separated, django_to_airdatepicker_datetime_separated,
) )
from apps.common.functions.format import get_format
class AirDatePickerInput(widgets.DateInput): class AirDatePickerInput(widgets.DateInput):
@@ -19,12 +19,10 @@ class AirDatePickerInput(widgets.DateInput):
format=None, format=None,
clear_button=True, clear_button=True,
auto_close=True, auto_close=True,
user=None,
*args, *args,
**kwargs, **kwargs,
): ):
attrs = attrs or {} attrs = attrs or {}
self.user = user
super().__init__(attrs=attrs, format=format, *args, **kwargs) super().__init__(attrs=attrs, format=format, *args, **kwargs)
self.clear_button = clear_button self.clear_button = clear_button
self.auto_close = auto_close self.auto_close = auto_close
@@ -41,12 +39,6 @@ class AirDatePickerInput(widgets.DateInput):
if self.format: if self.format:
return self.format return self.format
if self.user and hasattr(self.user, "settings"):
user_format = self.user.settings.date_format
if user_format == "SHORT_DATE_FORMAT":
return get_format("SHORT_DATE_FORMAT", use_l10n=True)
return user_format
return get_format("SHORT_DATE_FORMAT", use_l10n=True) return get_format("SHORT_DATE_FORMAT", use_l10n=True)
def build_attrs(self, base_attrs, extra_attrs=None): def build_attrs(self, base_attrs, extra_attrs=None):
@@ -97,12 +89,10 @@ class AirDateTimePickerInput(widgets.DateTimeInput):
timepicker=True, timepicker=True,
clear_button=True, clear_button=True,
auto_close=True, auto_close=True,
user=None,
*args, *args,
**kwargs, **kwargs,
): ):
attrs = attrs or {} attrs = attrs or {}
self.user = user
super().__init__(attrs=attrs, format=format, *args, **kwargs) super().__init__(attrs=attrs, format=format, *args, **kwargs)
self.timepicker = timepicker self.timepicker = timepicker
self.clear_button = clear_button self.clear_button = clear_button
@@ -120,12 +110,6 @@ class AirDateTimePickerInput(widgets.DateTimeInput):
if self.format: if self.format:
return self.format return self.format
if self.user and hasattr(self.user, "settings"):
user_format = self.user.settings.datetime_format
if user_format == "SHORT_DATETIME_FORMAT":
return get_format("SHORT_DATETIME_FORMAT", use_l10n=True)
return user_format
return get_format("SHORT_DATETIME_FORMAT", use_l10n=True) return get_format("SHORT_DATETIME_FORMAT", use_l10n=True)
def build_attrs(self, base_attrs, extra_attrs=None): def build_attrs(self, base_attrs, extra_attrs=None):
@@ -148,9 +132,14 @@ class AirDateTimePickerInput(widgets.DateTimeInput):
def format_value(self, value): def format_value(self, value):
"""Format the value for display in the widget.""" """Format the value for display in the widget."""
if value: if value and isinstance(value, (datetime.date, datetime.datetime)):
self.attrs["data-value"] = datetime.datetime.strftime( self.attrs["data-value"] = datetime.datetime.strftime(
value, "%Y-%m-%d %H:%M:00" value, "%Y-%m-%dT%H:%M:00"
)
elif value and isinstance(value, str):
value = datetime.datetime.strptime(value, "%Y-%m-%d %H:%M:00")
self.attrs["data-value"] = datetime.datetime.strftime(
value, "%Y-%m-%dT%H:%M:00"
) )
if value is None: if value is None:
@@ -195,6 +184,7 @@ class AirMonthYearPickerInput(AirDatePickerInput):
# Add data attributes for AirDatepicker configuration # Add data attributes for AirDatepicker configuration
attrs["data-now-button-txt"] = _("Today") attrs["data-now-button-txt"] = _("Today")
attrs["data-date-format"] = "MMMM yyyy"
return attrs return attrs

View File

@@ -1,7 +1,9 @@
from decimal import Decimal, InvalidOperation from decimal import Decimal, InvalidOperation
from django import forms from django import forms
from django.utils.formats import get_format, number_format from django.utils.formats import number_format
from apps.common.functions.format import get_format
def convert_to_decimal(value: str): def convert_to_decimal(value: str):

View File

@@ -72,7 +72,7 @@ class ExchangeRateForm(forms.ModelForm):
model = ExchangeRate model = ExchangeRate
fields = ["from_currency", "to_currency", "rate", "date"] fields = ["from_currency", "to_currency", "rate", "date"]
def __init__(self, *args, user=None, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.helper = FormHelper() self.helper = FormHelper()
@@ -81,9 +81,7 @@ class ExchangeRateForm(forms.ModelForm):
self.helper.layout = Layout("date", "from_currency", "to_currency", "rate") self.helper.layout = Layout("date", "from_currency", "to_currency", "rate")
self.fields["rate"].widget = ArbitraryDecimalDisplayNumberInput() self.fields["rate"].widget = ArbitraryDecimalDisplayNumberInput()
self.fields["date"].widget = AirDateTimePickerInput( self.fields["date"].widget = AirDateTimePickerInput(clear_button=False)
clear_button=False, user=user
)
if self.instance and self.instance.pk: if self.instance and self.instance.pk:
self.helper.layout.append( self.helper.layout.append(

View File

@@ -72,7 +72,9 @@ class ExchangeRate(models.Model):
def clean(self): def clean(self):
super().clean() super().clean()
if self.from_currency == self.to_currency: # Check if the attributes exist before comparing them
raise ValidationError( if hasattr(self, "from_currency") and hasattr(self, "to_currency"):
{"to_currency": _("From and To currencies cannot be the same.")} if self.from_currency == self.to_currency:
) raise ValidationError(
{"to_currency": _("From and To currencies cannot be the same.")}
)

View File

@@ -83,7 +83,7 @@ def exchange_rates_list_pair(request):
@require_http_methods(["GET", "POST"]) @require_http_methods(["GET", "POST"])
def exchange_rate_add(request): def exchange_rate_add(request):
if request.method == "POST": if request.method == "POST":
form = ExchangeRateForm(request.POST, user=request.user) form = ExchangeRateForm(request.POST)
if form.is_valid(): if form.is_valid():
form.save() form.save()
messages.success(request, _("Exchange rate added successfully")) messages.success(request, _("Exchange rate added successfully"))
@@ -95,7 +95,7 @@ def exchange_rate_add(request):
}, },
) )
else: else:
form = ExchangeRateForm(user=request.user) form = ExchangeRateForm()
return render( return render(
request, request,
@@ -111,7 +111,7 @@ def exchange_rate_edit(request, pk):
exchange_rate = get_object_or_404(ExchangeRate, id=pk) exchange_rate = get_object_or_404(ExchangeRate, id=pk)
if request.method == "POST": if request.method == "POST":
form = ExchangeRateForm(request.POST, instance=exchange_rate, user=request.user) form = ExchangeRateForm(request.POST, instance=exchange_rate)
if form.is_valid(): if form.is_valid():
form.save() form.save()
messages.success(request, _("Exchange rate updated successfully")) messages.success(request, _("Exchange rate updated successfully"))
@@ -123,7 +123,7 @@ def exchange_rate_edit(request, pk):
}, },
) )
else: else:
form = ExchangeRateForm(instance=exchange_rate, user=request.user) form = ExchangeRateForm(instance=exchange_rate)
return render( return render(
request, request,

View File

@@ -65,7 +65,7 @@ class DCAEntryForm(forms.ModelForm):
"notes": forms.Textarea(attrs={"rows": 3}), "notes": forms.Textarea(attrs={"rows": 3}),
} }
def __init__(self, *args, user=None, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.helper = FormHelper() self.helper = FormHelper()
self.helper.form_tag = False self.helper.form_tag = False
@@ -106,4 +106,4 @@ class DCAEntryForm(forms.ModelForm):
self.fields["amount_paid"].widget = ArbitraryDecimalDisplayNumberInput() self.fields["amount_paid"].widget = ArbitraryDecimalDisplayNumberInput()
self.fields["amount_received"].widget = ArbitraryDecimalDisplayNumberInput() self.fields["amount_received"].widget = ArbitraryDecimalDisplayNumberInput()
self.fields["date"].widget = AirDatePickerInput(clear_button=False, user=user) self.fields["date"].widget = AirDatePickerInput(clear_button=False)

View File

@@ -155,7 +155,7 @@ def strategy_detail(request, strategy_id):
def strategy_entry_add(request, strategy_id): def strategy_entry_add(request, strategy_id):
strategy = get_object_or_404(DCAStrategy, id=strategy_id) strategy = get_object_or_404(DCAStrategy, id=strategy_id)
if request.method == "POST": if request.method == "POST":
form = DCAEntryForm(request.POST, user=request.user) form = DCAEntryForm(request.POST)
if form.is_valid(): if form.is_valid():
entry = form.save(commit=False) entry = form.save(commit=False)
entry.strategy = strategy entry.strategy = strategy
@@ -169,7 +169,7 @@ def strategy_entry_add(request, strategy_id):
}, },
) )
else: else:
form = DCAEntryForm(user=request.user) form = DCAEntryForm()
return render( return render(
request, request,
@@ -184,7 +184,7 @@ def strategy_entry_edit(request, strategy_id, entry_id):
dca_entry = get_object_or_404(DCAEntry, id=entry_id, strategy__id=strategy_id) dca_entry = get_object_or_404(DCAEntry, id=entry_id, strategy__id=strategy_id)
if request.method == "POST": if request.method == "POST":
form = DCAEntryForm(request.POST, instance=dca_entry, user=request.user) form = DCAEntryForm(request.POST, instance=dca_entry)
if form.is_valid(): if form.is_valid():
form.save() form.save()
messages.success(request, _("Entry updated successfully")) messages.success(request, _("Entry updated successfully"))
@@ -196,7 +196,7 @@ def strategy_entry_edit(request, strategy_id, entry_id):
}, },
) )
else: else:
form = DCAEntryForm(instance=dca_entry, user=request.user) form = DCAEntryForm(instance=dca_entry)
return render( return render(
request, request,

View File

@@ -65,7 +65,7 @@ class CSVImportSettings(BaseModel):
class ColumnMapping(BaseModel): class ColumnMapping(BaseModel):
source: Optional[str] = Field( source: Optional[str] | Optional[list[str]] = Field(
default=None, default=None,
description="CSV column header. If None, the field will be generated from transformations", description="CSV column header. If None, the field will be generated from transformations",
) )

View File

@@ -486,8 +486,18 @@ class ImportService:
mapped_data = {} mapped_data = {}
for field, mapping in self.mapping.items(): for field, mapping in self.mapping.items():
# If source is None, use None as the initial value value = None
value = row.get(mapping.source) if mapping.source else None
if isinstance(mapping.source, str):
value = row.get(mapping.source)
elif isinstance(mapping.source, list):
for source in mapping.source:
value = row.get(source)
if value is not None:
break
else:
# If source is None, use None as the initial value
value = None
# Use default_value if value is None # Use default_value if value is None
if value is None: if value is None:

View File

@@ -30,6 +30,8 @@ def index(request):
@login_required @login_required
@require_http_methods(["GET"]) @require_http_methods(["GET"])
def monthly_overview(request, month: int, year: int): def monthly_overview(request, month: int, year: int):
order = request.session.get("monthly_transactions_order", "default")
if month < 1 or month > 12: if month < 1 or month > 12:
from django.http import Http404 from django.http import Http404
@@ -41,7 +43,7 @@ def monthly_overview(request, month: int, year: int):
previous_month = 12 if month == 1 else month - 1 previous_month = 12 if month == 1 else month - 1
previous_year = year - 1 if previous_month == 12 and month == 1 else year previous_year = year - 1 if previous_month == 12 and month == 1 else year
f = TransactionsFilter(request.GET, user=request.user) f = TransactionsFilter(request.GET)
return render( return render(
request, request,
@@ -54,6 +56,7 @@ def monthly_overview(request, month: int, year: int):
"previous_month": previous_month, "previous_month": previous_month,
"previous_year": previous_year, "previous_year": previous_year,
"filter": f, "filter": f,
"order": order,
}, },
) )
@@ -62,9 +65,14 @@ def monthly_overview(request, month: int, year: int):
@login_required @login_required
@require_http_methods(["GET"]) @require_http_methods(["GET"])
def transactions_list(request, month: int, year: int): def transactions_list(request, month: int, year: int):
order = request.GET.get("order") order = request.session.get("monthly_transactions_order", "default")
f = TransactionsFilter(request.GET, user=request.user) if "order" in request.GET:
order = request.GET["order"]
if order != request.session["monthly_transactions_order"]:
request.session["monthly_transactions_order"] = order
f = TransactionsFilter(request.GET)
transactions_filtered = ( transactions_filtered = (
f.qs.filter() f.qs.filter()
.filter( .filter(

View File

@@ -133,7 +133,7 @@ class TransactionsFilter(django_filters.FilterSet):
"to_amount", "to_amount",
] ]
def __init__(self, data=None, user=None, *args, **kwargs): def __init__(self, data=None, *args, **kwargs):
# if filterset is bound, use initial values as defaults # if filterset is bound, use initial values as defaults
if data is not None: if data is not None:
# get a mutable copy of the QueryDict # get a mutable copy of the QueryDict
@@ -182,5 +182,5 @@ class TransactionsFilter(django_filters.FilterSet):
self.form.fields["to_amount"].widget = ArbitraryDecimalDisplayNumberInput() self.form.fields["to_amount"].widget = ArbitraryDecimalDisplayNumberInput()
self.form.fields["from_amount"].widget = ArbitraryDecimalDisplayNumberInput() self.form.fields["from_amount"].widget = ArbitraryDecimalDisplayNumberInput()
self.form.fields["date_start"].widget = AirDatePickerInput(user=user) self.form.fields["date_start"].widget = AirDatePickerInput()
self.form.fields["date_end"].widget = AirDatePickerInput(user=user) self.form.fields["date_end"].widget = AirDatePickerInput()

View File

@@ -86,7 +86,7 @@ class TransactionForm(forms.ModelForm):
"account": TomSelect(clear_button=False, group_by="group"), "account": TomSelect(clear_button=False, group_by="group"),
} }
def __init__(self, *args, user=None, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# if editing a transaction display non-archived items and it's own item even if it's archived # if editing a transaction display non-archived items and it's own item even if it's archived
@@ -177,7 +177,7 @@ class TransactionForm(forms.ModelForm):
) )
self.fields["reference_date"].required = False self.fields["reference_date"].required = False
self.fields["date"].widget = AirDatePickerInput(clear_button=False, user=user) self.fields["date"].widget = AirDatePickerInput(clear_button=False)
if self.instance and self.instance.pk: if self.instance and self.instance.pk:
decimal_places = self.instance.account.currency.decimal_places decimal_places = self.instance.account.currency.decimal_places
@@ -333,7 +333,7 @@ class TransferForm(forms.Form):
label=_("Notes"), label=_("Notes"),
) )
def __init__(self, *args, user=None, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.helper = FormHelper() self.helper = FormHelper()
@@ -402,7 +402,7 @@ class TransferForm(forms.Form):
self.fields["from_amount"].widget = ArbitraryDecimalDisplayNumberInput() self.fields["from_amount"].widget = ArbitraryDecimalDisplayNumberInput()
self.fields["to_amount"].widget = ArbitraryDecimalDisplayNumberInput() self.fields["to_amount"].widget = ArbitraryDecimalDisplayNumberInput()
self.fields["date"].widget = AirDatePickerInput(clear_button=False, user=user) self.fields["date"].widget = AirDatePickerInput(clear_button=False)
def clean(self): def clean(self):
cleaned_data = super().clean() cleaned_data = super().clean()
@@ -515,7 +515,7 @@ class InstallmentPlanForm(forms.ModelForm):
"notes": forms.Textarea(attrs={"rows": 3}), "notes": forms.Textarea(attrs={"rows": 3}),
} }
def __init__(self, *args, user=None, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# if editing display non-archived items and it's own item even if it's archived # if editing display non-archived items and it's own item even if it's archived
@@ -572,9 +572,7 @@ class InstallmentPlanForm(forms.ModelForm):
) )
self.fields["installment_amount"].widget = ArbitraryDecimalDisplayNumberInput() self.fields["installment_amount"].widget = ArbitraryDecimalDisplayNumberInput()
self.fields["start_date"].widget = AirDatePickerInput( self.fields["start_date"].widget = AirDatePickerInput(clear_button=False)
clear_button=False, user=user
)
if self.instance and self.instance.pk: if self.instance and self.instance.pk:
self.helper.layout.append( self.helper.layout.append(
@@ -762,7 +760,7 @@ class RecurringTransactionForm(forms.ModelForm):
), ),
} }
def __init__(self, *args, user=None, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# if editing display non-archived items and it's own item even if it's archived # if editing display non-archived items and it's own item even if it's archived
@@ -819,10 +817,8 @@ class RecurringTransactionForm(forms.ModelForm):
) )
self.fields["amount"].widget = ArbitraryDecimalDisplayNumberInput() self.fields["amount"].widget = ArbitraryDecimalDisplayNumberInput()
self.fields["start_date"].widget = AirDatePickerInput( self.fields["start_date"].widget = AirDatePickerInput(clear_button=False)
clear_button=False, user=user self.fields["end_date"].widget = AirDatePickerInput()
)
self.fields["end_date"].widget = AirDatePickerInput(user=user)
if self.instance and self.instance.pk: if self.instance and self.instance.pk:
self.helper.layout.append( self.helper.layout.append(

View File

@@ -49,7 +49,7 @@ class SoftDeleteQuerySet(models.QuerySet):
class SoftDeleteManager(models.Manager): class SoftDeleteManager(models.Manager):
def get_queryset(self): def get_queryset(self):
qs = SoftDeleteQuerySet(self.model, using=self._db) qs = SoftDeleteQuerySet(self.model, using=self._db)
return qs if not settings.ENABLE_SOFT_DELETE else qs.filter(deleted=False) return qs.filter(deleted=False)
class AllObjectsManager(models.Manager): class AllObjectsManager(models.Manager):
@@ -60,7 +60,7 @@ class AllObjectsManager(models.Manager):
class DeletedObjectsManager(models.Manager): class DeletedObjectsManager(models.Manager):
def get_queryset(self): def get_queryset(self):
qs = SoftDeleteQuerySet(self.model, using=self._db) qs = SoftDeleteQuerySet(self.model, using=self._db)
return qs if not settings.ENABLE_SOFT_DELETE else qs.filter(deleted=True) return qs.filter(deleted=True)
class TransactionCategory(models.Model): class TransactionCategory(models.Model):

View File

@@ -27,7 +27,7 @@ def generate_recurring_transactions(timestamp=None):
@app.periodic(cron="10 1 * * *") @app.periodic(cron="10 1 * * *")
@app.task @app.task
def cleanup_deleted_transactions(): def cleanup_deleted_transactions(timestamp=None):
with cachalot_disabled(): with cachalot_disabled():
if settings.ENABLE_SOFT_DELETE and settings.KEEP_DELETED_TRANSACTIONS_FOR == 0: if settings.ENABLE_SOFT_DELETE and settings.KEEP_DELETED_TRANSACTIONS_FOR == 0:
return "KEEP_DELETED_TRANSACTIONS_FOR is 0, no cleanup performed." return "KEEP_DELETED_TRANSACTIONS_FOR is 0, no cleanup performed."
@@ -44,7 +44,7 @@ def cleanup_deleted_transactions():
days=settings.KEEP_DELETED_TRANSACTIONS_FOR days=settings.KEEP_DELETED_TRANSACTIONS_FOR
) )
invalidate("transactions.Transaction") invalidate()
# Hard delete soft-deleted transactions older than the cutoff date # Hard delete soft-deleted transactions older than the cutoff date
old_transactions = Transaction.deleted_objects.filter(deleted_at__lt=cutoff_date) old_transactions = Transaction.deleted_objects.filter(deleted_at__lt=cutoff_date)

View File

@@ -81,7 +81,7 @@ def installment_plan_transactions(request, installment_plan_id):
@require_http_methods(["GET", "POST"]) @require_http_methods(["GET", "POST"])
def installment_plan_add(request): def installment_plan_add(request):
if request.method == "POST": if request.method == "POST":
form = InstallmentPlanForm(request.POST, user=request.user) form = InstallmentPlanForm(request.POST)
if form.is_valid(): if form.is_valid():
form.save() form.save()
messages.success(request, _("Installment Plan added successfully")) messages.success(request, _("Installment Plan added successfully"))
@@ -93,7 +93,7 @@ def installment_plan_add(request):
}, },
) )
else: else:
form = InstallmentPlanForm(user=request.user) form = InstallmentPlanForm()
return render( return render(
request, request,
@@ -109,9 +109,7 @@ def installment_plan_edit(request, installment_plan_id):
installment_plan = get_object_or_404(InstallmentPlan, id=installment_plan_id) installment_plan = get_object_or_404(InstallmentPlan, id=installment_plan_id)
if request.method == "POST": if request.method == "POST":
form = InstallmentPlanForm( form = InstallmentPlanForm(request.POST, instance=installment_plan)
request.POST, instance=installment_plan, user=request.user
)
if form.is_valid(): if form.is_valid():
form.save() form.save()
messages.success(request, _("Installment Plan updated successfully")) messages.success(request, _("Installment Plan updated successfully"))
@@ -123,7 +121,7 @@ def installment_plan_edit(request, installment_plan_id):
}, },
) )
else: else:
form = InstallmentPlanForm(instance=installment_plan, user=request.user) form = InstallmentPlanForm(instance=installment_plan)
return render( return render(
request, request,

View File

@@ -106,7 +106,7 @@ def recurring_transaction_transactions(request, recurring_transaction_id):
@require_http_methods(["GET", "POST"]) @require_http_methods(["GET", "POST"])
def recurring_transaction_add(request): def recurring_transaction_add(request):
if request.method == "POST": if request.method == "POST":
form = RecurringTransactionForm(request.POST, user=request.user) form = RecurringTransactionForm(request.POST)
if form.is_valid(): if form.is_valid():
form.save() form.save()
messages.success(request, _("Recurring Transaction added successfully")) messages.success(request, _("Recurring Transaction added successfully"))
@@ -118,7 +118,7 @@ def recurring_transaction_add(request):
}, },
) )
else: else:
form = RecurringTransactionForm(user=request.user) form = RecurringTransactionForm()
return render( return render(
request, request,
@@ -136,9 +136,7 @@ def recurring_transaction_edit(request, recurring_transaction_id):
) )
if request.method == "POST": if request.method == "POST":
form = RecurringTransactionForm( form = RecurringTransactionForm(request.POST, instance=recurring_transaction)
request.POST, instance=recurring_transaction, user=request.user
)
if form.is_valid(): if form.is_valid():
form.save() form.save()
messages.success(request, _("Recurring Transaction updated successfully")) messages.success(request, _("Recurring Transaction updated successfully"))
@@ -150,9 +148,7 @@ def recurring_transaction_edit(request, recurring_transaction_id):
}, },
) )
else: else:
form = RecurringTransactionForm( form = RecurringTransactionForm(instance=recurring_transaction)
instance=recurring_transaction, user=request.user
)
return render( return render(
request, request,

View File

@@ -44,7 +44,7 @@ def transaction_add(request):
).date() ).date()
if request.method == "POST": if request.method == "POST":
form = TransactionForm(request.POST, user=request.user) form = TransactionForm(request.POST)
if form.is_valid(): if form.is_valid():
form.save() form.save()
messages.success(request, _("Transaction added successfully")) messages.success(request, _("Transaction added successfully"))
@@ -55,7 +55,6 @@ def transaction_add(request):
) )
else: else:
form = TransactionForm( form = TransactionForm(
user=request.user,
initial={ initial={
"date": expected_date, "date": expected_date,
"type": transaction_type, "type": transaction_type,
@@ -84,13 +83,12 @@ def transaction_simple_add(request):
).date() ).date()
if request.method == "POST": if request.method == "POST":
form = TransactionForm(request.POST, user=request.user) form = TransactionForm(request.POST)
if form.is_valid(): if form.is_valid():
form.save() form.save()
messages.success(request, _("Transaction added successfully")) messages.success(request, _("Transaction added successfully"))
form = TransactionForm( form = TransactionForm(
user=request.user,
initial={ initial={
"date": expected_date, "date": expected_date,
"type": transaction_type, "type": transaction_type,
@@ -99,7 +97,6 @@ def transaction_simple_add(request):
else: else:
form = TransactionForm( form = TransactionForm(
user=request.user,
initial={ initial={
"date": expected_date, "date": expected_date,
"type": transaction_type, "type": transaction_type,
@@ -120,7 +117,7 @@ def transaction_edit(request, transaction_id, **kwargs):
transaction = get_object_or_404(Transaction, id=transaction_id) transaction = get_object_or_404(Transaction, id=transaction_id)
if request.method == "POST": if request.method == "POST":
form = TransactionForm(request.POST, user=request.user, instance=transaction) form = TransactionForm(request.POST, instance=transaction)
if form.is_valid(): if form.is_valid():
form.save() form.save()
messages.success(request, _("Transaction updated successfully")) messages.success(request, _("Transaction updated successfully"))
@@ -130,7 +127,7 @@ def transaction_edit(request, transaction_id, **kwargs):
headers={"HX-Trigger": "updated, hide_offcanvas"}, headers={"HX-Trigger": "updated, hide_offcanvas"},
) )
else: else:
form = TransactionForm(instance=transaction, user=request.user) form = TransactionForm(instance=transaction)
return render( return render(
request, request,
@@ -152,7 +149,7 @@ def transactions_bulk_edit(request):
count = transactions.count() count = transactions.count()
if request.method == "POST": if request.method == "POST":
form = BulkEditTransactionForm(request.POST, user=request.user) form = BulkEditTransactionForm(request.POST)
if form.is_valid(): if form.is_valid():
# Apply changes from the form to all selected transactions # Apply changes from the form to all selected transactions
for transaction in transactions: for transaction in transactions:
@@ -184,9 +181,7 @@ def transactions_bulk_edit(request):
headers={"HX-Trigger": "updated, hide_offcanvas"}, headers={"HX-Trigger": "updated, hide_offcanvas"},
) )
else: else:
form = BulkEditTransactionForm( form = BulkEditTransactionForm(initial={"is_paid": None, "type": None})
initial={"is_paid": None, "type": None}, user=request.user
)
context = { context = {
"form": form, "form": form,
@@ -276,7 +271,7 @@ def transactions_transfer(request):
).date() ).date()
if request.method == "POST": if request.method == "POST":
form = TransferForm(request.POST, user=request.user) form = TransferForm(request.POST)
if form.is_valid(): if form.is_valid():
form.save() form.save()
messages.success(request, _("Transfer added successfully")) messages.success(request, _("Transfer added successfully"))
@@ -290,7 +285,6 @@ def transactions_transfer(request):
"reference_date": expected_date, "reference_date": expected_date,
"date": expected_date, "date": expected_date,
}, },
user=request.user,
) )
return render(request, "transactions/fragments/transfer.html", {"form": form}) return render(request, "transactions/fragments/transfer.html", {"form": form})
@@ -319,15 +313,23 @@ def transaction_pay(request, transaction_id):
@login_required @login_required
@require_http_methods(["GET"]) @require_http_methods(["GET"])
def transaction_all_index(request): def transaction_all_index(request):
f = TransactionsFilter(request.GET, user=request.user) order = request.session.get("all_transactions_order", "default")
return render(request, "transactions/pages/transactions.html", {"filter": f}) f = TransactionsFilter(request.GET)
return render(
request, "transactions/pages/transactions.html", {"filter": f, "order": order}
)
@only_htmx @only_htmx
@login_required @login_required
@require_http_methods(["GET"]) @require_http_methods(["GET"])
def transaction_all_list(request): def transaction_all_list(request):
order = request.GET.get("order") order = request.session.get("all_transactions_order", "default")
if "order" in request.GET:
order = request.GET["order"]
if order != request.session["all_transactions_order"]:
request.session["all_transactions_order"] = order
transactions = Transaction.objects.prefetch_related( transactions = Transaction.objects.prefetch_related(
"account", "account",
@@ -341,7 +343,7 @@ def transaction_all_list(request):
transactions = default_order(transactions, order=order) transactions = default_order(transactions, order=order)
f = TransactionsFilter(request.GET, user=request.user, queryset=transactions) f = TransactionsFilter(request.GET, queryset=transactions)
page_number = request.GET.get("page", 1) page_number = request.GET.get("page", 1)
paginator = Paginator(f.qs, 100) paginator = Paginator(f.qs, 100)
@@ -371,7 +373,7 @@ def transaction_all_summary(request):
"installment_plan", "installment_plan",
).all() ).all()
f = TransactionsFilter(request.GET, user=request.user, queryset=transactions) f = TransactionsFilter(request.GET, queryset=transactions)
currency_data = calculate_currency_totals(f.qs.all(), ignore_empty=True) currency_data = calculate_currency_totals(f.qs.all(), ignore_empty=True)
currency_percentages = calculate_percentage_distribution(currency_data) currency_percentages = calculate_percentage_distribution(currency_data)

View File

@@ -81,6 +81,12 @@ class UserSettingsForm(forms.ModelForm):
("Y.m.d h:i A", "2025.01.20 03:30 PM"), ("Y.m.d h:i A", "2025.01.20 03:30 PM"),
] ]
NUMBER_FORMAT_CHOICES = [
("AA", _("Default")),
("DC", "1.234,50"),
("CD", "1,234.50"),
]
date_format = forms.ChoiceField( date_format = forms.ChoiceField(
choices=DATE_FORMAT_CHOICES, initial="SHORT_DATE_FORMAT", label=_("Date Format") choices=DATE_FORMAT_CHOICES, initial="SHORT_DATE_FORMAT", label=_("Date Format")
) )
@@ -90,6 +96,12 @@ class UserSettingsForm(forms.ModelForm):
label=_("Datetime Format"), label=_("Datetime Format"),
) )
number_format = forms.ChoiceField(
choices=NUMBER_FORMAT_CHOICES,
initial="AA",
label=_("Number Format"),
)
class Meta: class Meta:
model = UserSettings model = UserSettings
fields = [ fields = [
@@ -98,6 +110,7 @@ class UserSettingsForm(forms.ModelForm):
"start_page", "start_page",
"date_format", "date_format",
"datetime_format", "datetime_format",
"number_format",
] ]
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@@ -111,6 +124,7 @@ class UserSettingsForm(forms.ModelForm):
"timezone", "timezone",
"date_format", "date_format",
"datetime_format", "datetime_format",
"number_format",
"start_page", "start_page",
FormActions( FormActions(
NoClassSubmit( NoClassSubmit(

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.5 on 2025-01-27 12:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0016_alter_usersettings_language'),
]
operations = [
migrations.AddField(
model_name='usersettings',
name='number_format',
field=models.CharField(default='AA', max_length=2, verbose_name='Number Format'),
),
]

View File

@@ -1,8 +1,8 @@
import pytz import pytz
from django.conf import settings from django.conf import settings
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.db import models
from django.contrib.auth.models import AbstractUser, Group from django.contrib.auth.models import AbstractUser, Group
from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from apps.users.managers import UserManager from apps.users.managers import UserManager
@@ -44,6 +44,9 @@ class UserSettings(models.Model):
default="SHORT_DATETIME_FORMAT", default="SHORT_DATETIME_FORMAT",
verbose_name=_("Datetime Format"), verbose_name=_("Datetime Format"),
) )
number_format = models.CharField(
max_length=2, default="AA", verbose_name=_("Number Format")
)
language = models.CharField( language = models.CharField(
max_length=10, max_length=10,
@@ -66,3 +69,6 @@ class UserSettings(models.Model):
def __str__(self): def __str__(self):
return f"{self.user.email}'s settings" return f"{self.user.email}'s settings"
def clean(self):
super().clean()

File diff suppressed because it is too large Load Diff

View File

@@ -8,9 +8,9 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-01-25 18:49+0000\n" "POT-Creation-Date: 2025-01-28 00:49+0000\n"
"PO-Revision-Date: 2025-01-26 12:50+0100\n" "PO-Revision-Date: 2025-01-27 13:27-0300\n"
"Last-Translator: Dimitri Decrock <dimitri@fam-decrock.eu>\n" "Last-Translator: Herculino Trotta\n"
"Language-Team: \n" "Language-Team: \n"
"Language: nl\n" "Language: nl\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@@ -24,28 +24,28 @@ msgid "Group name"
msgstr "Groepsnaam" msgstr "Groepsnaam"
#: apps/accounts/forms.py:40 apps/accounts/forms.py:96 #: apps/accounts/forms.py:40 apps/accounts/forms.py:96
#: apps/currencies/forms.py:52 apps/currencies/forms.py:92 apps/dca/forms.py:41 #: apps/currencies/forms.py:52 apps/currencies/forms.py:90 apps/dca/forms.py:41
#: apps/dca/forms.py:93 apps/import_app/forms.py:34 apps/rules/forms.py:45 #: apps/dca/forms.py:93 apps/import_app/forms.py:34 apps/rules/forms.py:45
#: apps/rules/forms.py:87 apps/transactions/forms.py:190 #: apps/rules/forms.py:87 apps/transactions/forms.py:190
#: apps/transactions/forms.py:257 apps/transactions/forms.py:583 #: apps/transactions/forms.py:257 apps/transactions/forms.py:581
#: apps/transactions/forms.py:626 apps/transactions/forms.py:658 #: apps/transactions/forms.py:624 apps/transactions/forms.py:656
#: apps/transactions/forms.py:693 apps/transactions/forms.py:831 #: apps/transactions/forms.py:691 apps/transactions/forms.py:827
msgid "Update" msgid "Update"
msgstr "Bijwerken" msgstr "Bijwerken"
#: apps/accounts/forms.py:48 apps/accounts/forms.py:104 #: apps/accounts/forms.py:48 apps/accounts/forms.py:104
#: apps/common/widgets/tom_select.py:12 apps/currencies/forms.py:60 #: apps/common/widgets/tom_select.py:12 apps/currencies/forms.py:60
#: apps/currencies/forms.py:100 apps/dca/forms.py:49 apps/dca/forms.py:102 #: apps/currencies/forms.py:98 apps/dca/forms.py:49 apps/dca/forms.py:102
#: apps/import_app/forms.py:42 apps/rules/forms.py:53 apps/rules/forms.py:95 #: apps/import_app/forms.py:42 apps/rules/forms.py:53 apps/rules/forms.py:95
#: apps/transactions/forms.py:174 apps/transactions/forms.py:199 #: apps/transactions/forms.py:174 apps/transactions/forms.py:199
#: apps/transactions/forms.py:591 apps/transactions/forms.py:634 #: apps/transactions/forms.py:589 apps/transactions/forms.py:632
#: apps/transactions/forms.py:666 apps/transactions/forms.py:701 #: apps/transactions/forms.py:664 apps/transactions/forms.py:699
#: apps/transactions/forms.py:839 #: apps/transactions/forms.py:835
#: templates/account_groups/fragments/list.html:9 #: templates/account_groups/fragments/list.html:9
#: templates/accounts/fragments/list.html:9 #: templates/accounts/fragments/list.html:9
#: templates/categories/fragments/list.html:9 #: templates/categories/fragments/list.html:9
#: templates/currencies/fragments/list.html:9 #: templates/currencies/fragments/list.html:9
#: templates/dca/fragments/strategy/details.html:38 #: templates/dca/fragments/strategy/details.html:37
#: templates/dca/fragments/strategy/list.html:9 #: templates/dca/fragments/strategy/list.html:9
#: templates/entities/fragments/list.html:9 #: templates/entities/fragments/list.html:9
#: templates/exchange_rates/fragments/list.html:10 #: templates/exchange_rates/fragments/list.html:10
@@ -69,7 +69,7 @@ msgstr "Nieuw saldo"
#: apps/accounts/forms.py:119 apps/rules/models.py:27 #: apps/accounts/forms.py:119 apps/rules/models.py:27
#: apps/transactions/forms.py:39 apps/transactions/forms.py:291 #: apps/transactions/forms.py:39 apps/transactions/forms.py:291
#: apps/transactions/forms.py:298 apps/transactions/forms.py:478 #: apps/transactions/forms.py:298 apps/transactions/forms.py:478
#: apps/transactions/forms.py:725 apps/transactions/models.py:159 #: apps/transactions/forms.py:723 apps/transactions/models.py:159
#: apps/transactions/models.py:311 apps/transactions/models.py:491 #: apps/transactions/models.py:311 apps/transactions/models.py:491
msgid "Category" msgid "Category"
msgstr "Categorie" msgstr "Categorie"
@@ -77,7 +77,7 @@ msgstr "Categorie"
#: apps/accounts/forms.py:126 apps/rules/models.py:28 #: apps/accounts/forms.py:126 apps/rules/models.py:28
#: apps/transactions/filters.py:74 apps/transactions/forms.py:47 #: apps/transactions/filters.py:74 apps/transactions/forms.py:47
#: apps/transactions/forms.py:307 apps/transactions/forms.py:315 #: apps/transactions/forms.py:307 apps/transactions/forms.py:315
#: apps/transactions/forms.py:471 apps/transactions/forms.py:718 #: apps/transactions/forms.py:471 apps/transactions/forms.py:716
#: apps/transactions/models.py:165 apps/transactions/models.py:313 #: apps/transactions/models.py:165 apps/transactions/models.py:313
#: apps/transactions/models.py:495 templates/includes/navbar.html:98 #: apps/transactions/models.py:495 templates/includes/navbar.html:98
#: templates/tags/fragments/list.html:5 templates/tags/pages/index.html:4 #: templates/tags/fragments/list.html:5 templates/tags/pages/index.html:4
@@ -151,7 +151,7 @@ msgstr ""
#: apps/accounts/models.py:59 apps/rules/models.py:19 #: apps/accounts/models.py:59 apps/rules/models.py:19
#: apps/transactions/forms.py:59 apps/transactions/forms.py:463 #: apps/transactions/forms.py:59 apps/transactions/forms.py:463
#: apps/transactions/forms.py:710 apps/transactions/models.py:132 #: apps/transactions/forms.py:708 apps/transactions/models.py:132
#: apps/transactions/models.py:271 apps/transactions/models.py:473 #: apps/transactions/models.py:271 apps/transactions/models.py:473
msgid "Account" msgid "Account"
msgstr "Rekening" msgstr "Rekening"
@@ -322,11 +322,17 @@ msgstr "Fout"
msgid "Info" msgid "Info"
msgstr "Info" msgstr "Info"
#: apps/common/widgets/datepicker.py:55 apps/common/widgets/datepicker.py:197 #: apps/common/views.py:110
#, fuzzy
#| msgid "Category updated successfully"
msgid "Cache cleared successfully"
msgstr "Categorie succesvol bijgewerkt"
#: apps/common/widgets/datepicker.py:47 apps/common/widgets/datepicker.py:186
msgid "Today" msgid "Today"
msgstr "Vandaag" msgstr "Vandaag"
#: apps/common/widgets/datepicker.py:139 #: apps/common/widgets/datepicker.py:123
msgid "Now" msgid "Now"
msgstr "Nu" msgstr "Nu"
@@ -356,8 +362,8 @@ msgstr "Achtervoegsel"
#: apps/currencies/forms.py:68 apps/dca/models.py:156 apps/rules/models.py:22 #: apps/currencies/forms.py:68 apps/dca/models.py:156 apps/rules/models.py:22
#: apps/transactions/forms.py:63 apps/transactions/forms.py:319 #: apps/transactions/forms.py:63 apps/transactions/forms.py:319
#: apps/transactions/models.py:142 #: apps/transactions/models.py:142
#: templates/dca/fragments/strategy/details.html:53 #: templates/dca/fragments/strategy/details.html:52
#: templates/exchange_rates/fragments/table.html:11 #: templates/exchange_rates/fragments/table.html:10
msgid "Date" msgid "Date"
msgstr "Datum" msgstr "Datum"
@@ -407,7 +413,7 @@ msgstr "Datum en Tijd"
msgid "Exchange Rates" msgid "Exchange Rates"
msgstr "Wisselkoersen" msgstr "Wisselkoersen"
#: apps/currencies/models.py:77 #: apps/currencies/models.py:79
msgid "From and To currencies cannot be the same." msgid "From and To currencies cannot be the same."
msgstr "Van en Naar munteenheid kunnen niet dezelfde zijn." msgstr "Van en Naar munteenheid kunnen niet dezelfde zijn."
@@ -461,11 +467,11 @@ msgstr "DCA Strategieën"
msgid "Strategy" msgid "Strategy"
msgstr "Strategie" msgstr "Strategie"
#: apps/dca/models.py:158 templates/dca/fragments/strategy/details.html:55 #: apps/dca/models.py:158 templates/dca/fragments/strategy/details.html:54
msgid "Amount Paid" msgid "Amount Paid"
msgstr "Betaald bedrag" msgstr "Betaald bedrag"
#: apps/dca/models.py:161 templates/dca/fragments/strategy/details.html:54 #: apps/dca/models.py:161 templates/dca/fragments/strategy/details.html:53
msgid "Amount Received" msgid "Amount Received"
msgstr "Ontvangen bedrag" msgstr "Ontvangen bedrag"
@@ -643,7 +649,7 @@ msgstr "Bedrag"
#: apps/rules/models.py:29 apps/transactions/filters.py:81 #: apps/rules/models.py:29 apps/transactions/filters.py:81
#: apps/transactions/forms.py:55 apps/transactions/forms.py:486 #: apps/transactions/forms.py:55 apps/transactions/forms.py:486
#: apps/transactions/forms.py:733 apps/transactions/models.py:117 #: apps/transactions/forms.py:731 apps/transactions/models.py:117
#: apps/transactions/models.py:170 apps/transactions/models.py:316 #: apps/transactions/models.py:170 apps/transactions/models.py:316
#: apps/transactions/models.py:498 templates/entities/fragments/list.html:5 #: apps/transactions/models.py:498 templates/entities/fragments/list.html:5
#: templates/entities/pages/index.html:4 templates/includes/navbar.html:100 #: templates/entities/pages/index.html:4 templates/includes/navbar.html:100
@@ -761,23 +767,23 @@ msgstr "Overschrijving"
msgid "From and To accounts must be different." msgid "From and To accounts must be different."
msgstr "Van en Naar rekening moeten verschillend zijn." msgstr "Van en Naar rekening moeten verschillend zijn."
#: apps/transactions/forms.py:612 #: apps/transactions/forms.py:610
msgid "Tag name" msgid "Tag name"
msgstr "Labelnaam" msgstr "Labelnaam"
#: apps/transactions/forms.py:644 #: apps/transactions/forms.py:642
msgid "Entity name" msgid "Entity name"
msgstr "Naam van bedrijf" msgstr "Naam van bedrijf"
#: apps/transactions/forms.py:676 #: apps/transactions/forms.py:674
msgid "Category name" msgid "Category name"
msgstr "Naam van categorie" msgstr "Naam van categorie"
#: apps/transactions/forms.py:678 #: apps/transactions/forms.py:676
msgid "Muted categories won't count towards your monthly total" msgid "Muted categories won't count towards your monthly total"
msgstr "Gedempte categorieën tellen niet mee voor je maandtotaal" msgstr "Gedempte categorieën tellen niet mee voor je maandtotaal"
#: apps/transactions/forms.py:850 #: apps/transactions/forms.py:846
msgid "End date should be after the start date" msgid "End date should be after the start date"
msgstr "De einddatum moet na de begindatum vallen" msgstr "De einddatum moet na de begindatum vallen"
@@ -1046,15 +1052,15 @@ msgstr "Bedrijf succesvol verwijderd"
msgid "Installment Plan added successfully" msgid "Installment Plan added successfully"
msgstr "Afbetalingsplan succesvol toegevoegd" msgstr "Afbetalingsplan succesvol toegevoegd"
#: apps/transactions/views/installment_plans.py:117 #: apps/transactions/views/installment_plans.py:115
msgid "Installment Plan updated successfully" msgid "Installment Plan updated successfully"
msgstr "Afbetalingsplan succesvol bijgewerkt" msgstr "Afbetalingsplan succesvol bijgewerkt"
#: apps/transactions/views/installment_plans.py:142 #: apps/transactions/views/installment_plans.py:140
msgid "Installment Plan refreshed successfully" msgid "Installment Plan refreshed successfully"
msgstr "Afbetalingsplan succesvol vernieuwd" msgstr "Afbetalingsplan succesvol vernieuwd"
#: apps/transactions/views/installment_plans.py:160 #: apps/transactions/views/installment_plans.py:158
msgid "Installment Plan deleted successfully" msgid "Installment Plan deleted successfully"
msgstr "Afbetalingsplan succesvol verwijderd" msgstr "Afbetalingsplan succesvol verwijderd"
@@ -1062,23 +1068,23 @@ msgstr "Afbetalingsplan succesvol verwijderd"
msgid "Recurring Transaction added successfully" msgid "Recurring Transaction added successfully"
msgstr "Terugkerende Verrichting succesvol toegevoegd" msgstr "Terugkerende Verrichting succesvol toegevoegd"
#: apps/transactions/views/recurring_transactions.py:144 #: apps/transactions/views/recurring_transactions.py:142
msgid "Recurring Transaction updated successfully" msgid "Recurring Transaction updated successfully"
msgstr "Terugkerende Verrichting succesvol bijgewerkt" msgstr "Terugkerende Verrichting succesvol bijgewerkt"
#: apps/transactions/views/recurring_transactions.py:190 #: apps/transactions/views/recurring_transactions.py:186
msgid "Recurring transaction unpaused successfully" msgid "Recurring transaction unpaused successfully"
msgstr "Terugkerende Verrichting succesvol hervat" msgstr "Terugkerende Verrichting succesvol hervat"
#: apps/transactions/views/recurring_transactions.py:193 #: apps/transactions/views/recurring_transactions.py:189
msgid "Recurring transaction paused successfully" msgid "Recurring transaction paused successfully"
msgstr "Terugkerende Verrichting succesvol onderbroken" msgstr "Terugkerende Verrichting succesvol onderbroken"
#: apps/transactions/views/recurring_transactions.py:219 #: apps/transactions/views/recurring_transactions.py:215
msgid "Recurring transaction finished successfully" msgid "Recurring transaction finished successfully"
msgstr "Terugkerende Verrichting succesvol voltooid" msgstr "Terugkerende Verrichting succesvol voltooid"
#: apps/transactions/views/recurring_transactions.py:239 #: apps/transactions/views/recurring_transactions.py:235
msgid "Recurring Transaction deleted successfully" msgid "Recurring Transaction deleted successfully"
msgstr "Terugkerende Verrichting succesvol verwijderd" msgstr "Terugkerende Verrichting succesvol verwijderd"
@@ -1095,30 +1101,30 @@ msgid "Tag deleted successfully"
msgstr "Label succesvol verwijderd" msgstr "Label succesvol verwijderd"
#: apps/transactions/views/transactions.py:50 #: apps/transactions/views/transactions.py:50
#: apps/transactions/views/transactions.py:90 #: apps/transactions/views/transactions.py:89
msgid "Transaction added successfully" msgid "Transaction added successfully"
msgstr "Verrichting succesvol toegevoegd" msgstr "Verrichting succesvol toegevoegd"
#: apps/transactions/views/transactions.py:126 #: apps/transactions/views/transactions.py:123
msgid "Transaction updated successfully" msgid "Transaction updated successfully"
msgstr "Verrichting succesvol bijgewerkt" msgstr "Verrichting succesvol bijgewerkt"
#: apps/transactions/views/transactions.py:176 #: apps/transactions/views/transactions.py:173
#, python-format #, python-format
msgid "%(count)s transaction updated successfully" msgid "%(count)s transaction updated successfully"
msgid_plural "%(count)s transactions updated successfully" msgid_plural "%(count)s transactions updated successfully"
msgstr[0] "%(count)s verrichting succesvol bijgewerkt" msgstr[0] "%(count)s verrichting succesvol bijgewerkt"
msgstr[1] "%(count)s verrichtingen succesvol bijgewerkt" msgstr[1] "%(count)s verrichtingen succesvol bijgewerkt"
#: apps/transactions/views/transactions.py:214 #: apps/transactions/views/transactions.py:209
msgid "Transaction duplicated successfully" msgid "Transaction duplicated successfully"
msgstr "Verrichting succesvol gedupliceerd" msgstr "Verrichting succesvol gedupliceerd"
#: apps/transactions/views/transactions.py:256 #: apps/transactions/views/transactions.py:251
msgid "Transaction deleted successfully" msgid "Transaction deleted successfully"
msgstr "Verrichting succesvol verwijderd" msgstr "Verrichting succesvol verwijderd"
#: apps/transactions/views/transactions.py:282 #: apps/transactions/views/transactions.py:277
msgid "Transfer added successfully" msgid "Transfer added successfully"
msgstr "Transactie succesvol toegevoegd" msgstr "Transactie succesvol toegevoegd"
@@ -1158,21 +1164,25 @@ msgstr "Ongeldig e-mailadres of wachtwoord"
msgid "This account is deactivated" msgid "This account is deactivated"
msgstr "Deze gebruiker is gedeactiveerd" msgstr "Deze gebruiker is gedeactiveerd"
#: apps/users/forms.py:50 apps/users/forms.py:63 #: apps/users/forms.py:50 apps/users/forms.py:63 apps/users/forms.py:85
#: templates/monthly_overview/pages/overview.html:116 #: templates/monthly_overview/pages/overview.html:116
#: templates/transactions/pages/transactions.html:35 #: templates/transactions/pages/transactions.html:35
msgid "Default" msgid "Default"
msgstr "Standaard" msgstr "Standaard"
#: apps/users/forms.py:85 apps/users/models.py:40 #: apps/users/forms.py:91 apps/users/models.py:40
msgid "Date Format" msgid "Date Format"
msgstr "Datumnotatie" msgstr "Datumnotatie"
#: apps/users/forms.py:90 apps/users/models.py:45 #: apps/users/forms.py:96 apps/users/models.py:45
msgid "Datetime Format" msgid "Datetime Format"
msgstr "Tijdsnotatie" msgstr "Tijdsnotatie"
#: apps/users/forms.py:117 #: apps/users/forms.py:102 apps/users/models.py:48
msgid "Number Format"
msgstr "Nummerformat"
#: apps/users/forms.py:131
msgid "Save" msgid "Save"
msgstr "Opslaan" msgstr "Opslaan"
@@ -1196,19 +1206,19 @@ msgstr "Alle Verrichtingen"
msgid "Calendar" msgid "Calendar"
msgstr "Kalender" msgstr "Kalender"
#: apps/users/models.py:50 apps/users/models.py:56 #: apps/users/models.py:53 apps/users/models.py:59
msgid "Auto" msgid "Auto"
msgstr "Automatisch" msgstr "Automatisch"
#: apps/users/models.py:52 #: apps/users/models.py:55
msgid "Language" msgid "Language"
msgstr "Taal" msgstr "Taal"
#: apps/users/models.py:58 #: apps/users/models.py:61
msgid "Time Zone" msgid "Time Zone"
msgstr "Tijdszone" msgstr "Tijdszone"
#: apps/users/models.py:64 #: apps/users/models.py:67
msgid "Start page" msgid "Start page"
msgstr "Startpagina" msgstr "Startpagina"
@@ -1244,9 +1254,9 @@ msgstr "Rekeningsgroep bewerken"
#: templates/accounts/fragments/list.html:37 #: templates/accounts/fragments/list.html:37
#: templates/categories/fragments/table.html:24 #: templates/categories/fragments/table.html:24
#: templates/currencies/fragments/list.html:33 #: templates/currencies/fragments/list.html:33
#: templates/dca/fragments/strategy/details.html:64 #: templates/dca/fragments/strategy/details.html:63
#: templates/entities/fragments/table.html:23 #: templates/entities/fragments/table.html:23
#: templates/exchange_rates/fragments/table.html:20 #: templates/exchange_rates/fragments/table.html:19
#: templates/import_app/fragments/profiles/list.html:44 #: templates/import_app/fragments/profiles/list.html:44
#: templates/installment_plans/fragments/table.html:23 #: templates/installment_plans/fragments/table.html:23
#: templates/recurring_transactions/fragments/table.html:25 #: templates/recurring_transactions/fragments/table.html:25
@@ -1258,13 +1268,13 @@ msgstr "Acties"
#: templates/account_groups/fragments/list.html:36 #: templates/account_groups/fragments/list.html:36
#: templates/accounts/fragments/list.html:41 #: templates/accounts/fragments/list.html:41
#: templates/categories/fragments/table.html:29 #: templates/categories/fragments/table.html:29
#: templates/cotton/transaction/item.html:110 #: templates/cotton/transaction/item.html:109
#: templates/cotton/ui/transactions_action_bar.html:43 #: templates/cotton/ui/transactions_action_bar.html:47
#: templates/currencies/fragments/list.html:37 #: templates/currencies/fragments/list.html:37
#: templates/dca/fragments/strategy/details.html:68 #: templates/dca/fragments/strategy/details.html:67
#: templates/dca/fragments/strategy/list.html:34 #: templates/dca/fragments/strategy/list.html:34
#: templates/entities/fragments/table.html:28 #: templates/entities/fragments/table.html:28
#: templates/exchange_rates/fragments/table.html:24 #: templates/exchange_rates/fragments/table.html:23
#: templates/import_app/fragments/profiles/list.html:48 #: templates/import_app/fragments/profiles/list.html:48
#: templates/installment_plans/fragments/table.html:27 #: templates/installment_plans/fragments/table.html:27
#: templates/recurring_transactions/fragments/table.html:29 #: templates/recurring_transactions/fragments/table.html:29
@@ -1277,13 +1287,13 @@ msgstr "Bijwerken"
#: templates/account_groups/fragments/list.html:43 #: templates/account_groups/fragments/list.html:43
#: templates/accounts/fragments/list.html:48 #: templates/accounts/fragments/list.html:48
#: templates/categories/fragments/table.html:36 #: templates/categories/fragments/table.html:36
#: templates/cotton/transaction/item.html:125 #: templates/cotton/transaction/item.html:124
#: templates/cotton/ui/transactions_action_bar.html:80 #: templates/cotton/ui/transactions_action_bar.html:84
#: templates/currencies/fragments/list.html:44 #: templates/currencies/fragments/list.html:44
#: templates/dca/fragments/strategy/details.html:76 #: templates/dca/fragments/strategy/details.html:75
#: templates/dca/fragments/strategy/list.html:42 #: templates/dca/fragments/strategy/list.html:42
#: templates/entities/fragments/table.html:36 #: templates/entities/fragments/table.html:36
#: templates/exchange_rates/fragments/table.html:32 #: templates/exchange_rates/fragments/table.html:31
#: templates/import_app/fragments/profiles/list.html:69 #: templates/import_app/fragments/profiles/list.html:69
#: templates/import_app/fragments/runs/list.html:102 #: templates/import_app/fragments/runs/list.html:102
#: templates/installment_plans/fragments/table.html:56 #: templates/installment_plans/fragments/table.html:56
@@ -1298,13 +1308,13 @@ msgstr "Verwijderen"
#: templates/account_groups/fragments/list.html:47 #: templates/account_groups/fragments/list.html:47
#: templates/accounts/fragments/list.html:52 #: templates/accounts/fragments/list.html:52
#: templates/categories/fragments/table.html:41 #: templates/categories/fragments/table.html:41
#: templates/cotton/transaction/item.html:129 #: templates/cotton/transaction/item.html:128
#: templates/cotton/ui/transactions_action_bar.html:82 #: templates/cotton/ui/transactions_action_bar.html:86
#: templates/currencies/fragments/list.html:48 #: templates/currencies/fragments/list.html:48
#: templates/dca/fragments/strategy/details.html:81 #: templates/dca/fragments/strategy/details.html:80
#: templates/dca/fragments/strategy/list.html:46 #: templates/dca/fragments/strategy/list.html:46
#: templates/entities/fragments/table.html:40 #: templates/entities/fragments/table.html:40
#: templates/exchange_rates/fragments/table.html:37 #: templates/exchange_rates/fragments/table.html:36
#: templates/import_app/fragments/profiles/list.html:73 #: templates/import_app/fragments/profiles/list.html:73
#: templates/import_app/fragments/runs/list.html:106 #: templates/import_app/fragments/runs/list.html:106
#: templates/installment_plans/fragments/table.html:48 #: templates/installment_plans/fragments/table.html:48
@@ -1322,13 +1332,13 @@ msgstr "Weet je het zeker?"
#: templates/account_groups/fragments/list.html:48 #: templates/account_groups/fragments/list.html:48
#: templates/accounts/fragments/list.html:53 #: templates/accounts/fragments/list.html:53
#: templates/categories/fragments/table.html:42 #: templates/categories/fragments/table.html:42
#: templates/cotton/transaction/item.html:130 #: templates/cotton/transaction/item.html:129
#: templates/cotton/ui/transactions_action_bar.html:83 #: templates/cotton/ui/transactions_action_bar.html:87
#: templates/currencies/fragments/list.html:49 #: templates/currencies/fragments/list.html:49
#: templates/dca/fragments/strategy/details.html:82 #: templates/dca/fragments/strategy/details.html:81
#: templates/dca/fragments/strategy/list.html:47 #: templates/dca/fragments/strategy/list.html:47
#: templates/entities/fragments/table.html:41 #: templates/entities/fragments/table.html:41
#: templates/exchange_rates/fragments/table.html:38 #: templates/exchange_rates/fragments/table.html:37
#: templates/import_app/fragments/profiles/list.html:74 #: templates/import_app/fragments/profiles/list.html:74
#: templates/rules/fragments/list.html:49 #: templates/rules/fragments/list.html:49
#: templates/rules/fragments/transaction_rule/view.html:61 #: templates/rules/fragments/transaction_rule/view.html:61
@@ -1339,12 +1349,12 @@ msgstr "Je kunt dit niet meer terugdraaien!"
#: templates/account_groups/fragments/list.html:49 #: templates/account_groups/fragments/list.html:49
#: templates/accounts/fragments/list.html:54 #: templates/accounts/fragments/list.html:54
#: templates/categories/fragments/table.html:43 #: templates/categories/fragments/table.html:43
#: templates/cotton/transaction/item.html:131 #: templates/cotton/transaction/item.html:130
#: templates/currencies/fragments/list.html:50 #: templates/currencies/fragments/list.html:50
#: templates/dca/fragments/strategy/details.html:83 #: templates/dca/fragments/strategy/details.html:82
#: templates/dca/fragments/strategy/list.html:48 #: templates/dca/fragments/strategy/list.html:48
#: templates/entities/fragments/table.html:42 #: templates/entities/fragments/table.html:42
#: templates/exchange_rates/fragments/table.html:39 #: templates/exchange_rates/fragments/table.html:38
#: templates/import_app/fragments/profiles/list.html:75 #: templates/import_app/fragments/profiles/list.html:75
#: templates/import_app/fragments/runs/list.html:108 #: templates/import_app/fragments/runs/list.html:108
#: templates/installment_plans/fragments/table.html:62 #: templates/installment_plans/fragments/table.html:62
@@ -1419,11 +1429,11 @@ msgstr "ZAT"
msgid "SUN" msgid "SUN"
msgstr "ZON" msgstr "ZON"
#: templates/calendar_view/fragments/list_transactions.html:6 #: templates/calendar_view/fragments/list_transactions.html:5
msgid "Transactions on" msgid "Transactions on"
msgstr "Verrichtingen op" msgstr "Verrichtingen op"
#: templates/calendar_view/fragments/list_transactions.html:16 #: templates/calendar_view/fragments/list_transactions.html:15
msgid "No transactions on this date" msgid "No transactions on this date"
msgstr "Geen verrichtingen op deze datum" msgstr "Geen verrichtingen op deze datum"
@@ -1482,12 +1492,12 @@ msgstr "Sluiten"
msgid "Search" msgid "Search"
msgstr "Zoeken" msgstr "Zoeken"
#: templates/cotton/transaction/item.html:6 #: templates/cotton/transaction/item.html:5
msgid "Select" msgid "Select"
msgstr "Selecteer" msgstr "Selecteer"
#: templates/cotton/transaction/item.html:117 #: templates/cotton/transaction/item.html:116
#: templates/cotton/ui/transactions_action_bar.html:72 #: templates/cotton/ui/transactions_action_bar.html:76
msgid "Duplicate" msgid "Duplicate"
msgstr "Dupliceren" msgstr "Dupliceren"
@@ -1511,62 +1521,62 @@ msgstr "Verwachte uitgaven"
msgid "Current Expenses" msgid "Current Expenses"
msgstr "Huidige uitgaven" msgstr "Huidige uitgaven"
#: templates/cotton/ui/transactions_action_bar.html:25 #: templates/cotton/ui/transactions_action_bar.html:29
msgid "Select All" msgid "Select All"
msgstr "Alles selecteren" msgstr "Alles selecteren"
#: templates/cotton/ui/transactions_action_bar.html:31 #: templates/cotton/ui/transactions_action_bar.html:35
msgid "Unselect All" msgid "Unselect All"
msgstr "Alles deselecteren" msgstr "Alles deselecteren"
#: templates/cotton/ui/transactions_action_bar.html:48 #: templates/cotton/ui/transactions_action_bar.html:52
#: templates/cotton/ui/transactions_action_bar.html:139 #: templates/cotton/ui/transactions_action_bar.html:143
msgid "Toggle Dropdown" msgid "Toggle Dropdown"
msgstr "In- Uitklapbaar" msgstr "In- Uitklapbaar"
#: templates/cotton/ui/transactions_action_bar.html:56 #: templates/cotton/ui/transactions_action_bar.html:60
msgid "Mark as unpaid" msgid "Mark as unpaid"
msgstr "Markeren als niet betaald" msgstr "Markeren als niet betaald"
#: templates/cotton/ui/transactions_action_bar.html:63 #: templates/cotton/ui/transactions_action_bar.html:67
msgid "Mark as paid" msgid "Mark as paid"
msgstr "Markeren als betaald" msgstr "Markeren als betaald"
#: templates/cotton/ui/transactions_action_bar.html:84 #: templates/cotton/ui/transactions_action_bar.html:88
msgid "Yes, delete them!" msgid "Yes, delete them!"
msgstr "Ja, verwijder ze!" msgstr "Ja, verwijder ze!"
#: templates/cotton/ui/transactions_action_bar.html:130 #: templates/cotton/ui/transactions_action_bar.html:134
#: templates/cotton/ui/transactions_action_bar.html:154 #: templates/cotton/ui/transactions_action_bar.html:158
#: templates/cotton/ui/transactions_action_bar.html:174 #: templates/cotton/ui/transactions_action_bar.html:178
#: templates/cotton/ui/transactions_action_bar.html:194 #: templates/cotton/ui/transactions_action_bar.html:198
#: templates/cotton/ui/transactions_action_bar.html:214 #: templates/cotton/ui/transactions_action_bar.html:218
#: templates/cotton/ui/transactions_action_bar.html:234 #: templates/cotton/ui/transactions_action_bar.html:238
#: templates/cotton/ui/transactions_action_bar.html:254 #: templates/cotton/ui/transactions_action_bar.html:258
msgid "copied!" msgid "copied!"
msgstr "gekopieerd!" msgstr "gekopieerd!"
#: templates/cotton/ui/transactions_action_bar.html:147 #: templates/cotton/ui/transactions_action_bar.html:151
msgid "Flat Total" msgid "Flat Total"
msgstr "Vast Totaal" msgstr "Vast Totaal"
#: templates/cotton/ui/transactions_action_bar.html:167 #: templates/cotton/ui/transactions_action_bar.html:171
msgid "Real Total" msgid "Real Total"
msgstr "Werkelijk Totaal" msgstr "Werkelijk Totaal"
#: templates/cotton/ui/transactions_action_bar.html:187 #: templates/cotton/ui/transactions_action_bar.html:191
msgid "Mean" msgid "Mean"
msgstr "Gemiddelde" msgstr "Gemiddelde"
#: templates/cotton/ui/transactions_action_bar.html:207 #: templates/cotton/ui/transactions_action_bar.html:211
msgid "Max" msgid "Max"
msgstr "Maximaal" msgstr "Maximaal"
#: templates/cotton/ui/transactions_action_bar.html:227 #: templates/cotton/ui/transactions_action_bar.html:231
msgid "Min" msgid "Min"
msgstr "Minimaal" msgstr "Minimaal"
#: templates/cotton/ui/transactions_action_bar.html:247 #: templates/cotton/ui/transactions_action_bar.html:251
msgid "Count" msgid "Count"
msgstr "Rekenen" msgstr "Rekenen"
@@ -1598,91 +1608,91 @@ msgstr "DCA-item bewerken"
msgid "Add DCA strategy" msgid "Add DCA strategy"
msgstr "DCA-strategie toevoegen" msgstr "DCA-strategie toevoegen"
#: templates/dca/fragments/strategy/details.html:23 #: templates/dca/fragments/strategy/details.html:22
msgid "No exchange rate available" msgid "No exchange rate available"
msgstr "Geen wisselkoers beschikbaar" msgstr "Geen wisselkoers beschikbaar"
#: templates/dca/fragments/strategy/details.html:34 #: templates/dca/fragments/strategy/details.html:33
msgid "Entries" msgid "Entries"
msgstr "Idems" msgstr "Idems"
#: templates/dca/fragments/strategy/details.html:56 #: templates/dca/fragments/strategy/details.html:55
msgid "Current Value" msgid "Current Value"
msgstr "Actuele waarde" msgstr "Actuele waarde"
#: templates/dca/fragments/strategy/details.html:57 #: templates/dca/fragments/strategy/details.html:56
msgid "P/L" msgid "P/L"
msgstr "W&V" msgstr "W&V"
#: templates/dca/fragments/strategy/details.html:125 #: templates/dca/fragments/strategy/details.html:124
msgid "No entries for this DCA" msgid "No entries for this DCA"
msgstr "Geen idems in deze DCA" msgstr "Geen idems in deze DCA"
#: templates/dca/fragments/strategy/details.html:126 #: templates/dca/fragments/strategy/details.html:125
#: templates/monthly_overview/fragments/list.html:41 #: templates/monthly_overview/fragments/list.html:41
#: templates/transactions/fragments/list_all.html:40 #: templates/transactions/fragments/list_all.html:40
msgid "Try adding one" msgid "Try adding one"
msgstr "Probeer er een toe te voegen" msgstr "Probeer er een toe te voegen"
#: templates/dca/fragments/strategy/details.html:136 #: templates/dca/fragments/strategy/details.html:135
msgid "Total Invested" msgid "Total Invested"
msgstr "Totaal Geïnvesteerd" msgstr "Totaal Geïnvesteerd"
#: templates/dca/fragments/strategy/details.html:150 #: templates/dca/fragments/strategy/details.html:149
msgid "Total Received" msgid "Total Received"
msgstr "Totaal Ontvangen" msgstr "Totaal Ontvangen"
#: templates/dca/fragments/strategy/details.html:164 #: templates/dca/fragments/strategy/details.html:163
msgid "Current Total Value" msgid "Current Total Value"
msgstr "Huidige Totaalwaarde" msgstr "Huidige Totaalwaarde"
#: templates/dca/fragments/strategy/details.html:178 #: templates/dca/fragments/strategy/details.html:177
msgid "Average Entry Price" msgid "Average Entry Price"
msgstr "Gemiddelde Instapprijs" msgstr "Gemiddelde Instapprijs"
#: templates/dca/fragments/strategy/details.html:192 #: templates/dca/fragments/strategy/details.html:191
msgid "Total P/L" msgid "Total P/L"
msgstr "Totaal W&V" msgstr "Totaal W&V"
#: templates/dca/fragments/strategy/details.html:208 #: templates/dca/fragments/strategy/details.html:207
#, python-format #, python-format
msgid "Total %% P/L" msgid "Total %% P/L"
msgstr "Totaal %% W&V" msgstr "Totaal %% W&V"
#: templates/dca/fragments/strategy/details.html:227 #: templates/dca/fragments/strategy/details.html:226
#, python-format #, python-format
msgid "P/L %%" msgid "P/L %%"
msgstr "W&V %%" msgstr "W&V %%"
#: templates/dca/fragments/strategy/details.html:289 #: templates/dca/fragments/strategy/details.html:288
msgid "Performance Over Time" msgid "Performance Over Time"
msgstr "Prestaties Na Verloop Van Tijd" msgstr "Prestaties Na Verloop Van Tijd"
#: templates/dca/fragments/strategy/details.html:307 #: templates/dca/fragments/strategy/details.html:306
msgid "Entry Price" msgid "Entry Price"
msgstr "Ingangsprijs" msgstr "Ingangsprijs"
#: templates/dca/fragments/strategy/details.html:315 #: templates/dca/fragments/strategy/details.html:314
msgid "Current Price" msgid "Current Price"
msgstr "Actuele Prijs" msgstr "Actuele Prijs"
#: templates/dca/fragments/strategy/details.html:323 #: templates/dca/fragments/strategy/details.html:322
msgid "Amount Bought" msgid "Amount Bought"
msgstr "Gekocht Bedrag" msgstr "Gekocht Bedrag"
#: templates/dca/fragments/strategy/details.html:391 #: templates/dca/fragments/strategy/details.html:390
msgid "Entry Price vs Current Price" msgid "Entry Price vs Current Price"
msgstr "Instapprijs vs Huidige Prijs" msgstr "Instapprijs vs Huidige Prijs"
#: templates/dca/fragments/strategy/details.html:407 #: templates/dca/fragments/strategy/details.html:406
msgid "Days Between Investments" msgid "Days Between Investments"
msgstr "Dagen Tussen Investeringen" msgstr "Dagen Tussen Investeringen"
#: templates/dca/fragments/strategy/details.html:454 #: templates/dca/fragments/strategy/details.html:453
msgid "Investment Frequency" msgid "Investment Frequency"
msgstr "Investeringsfrequentie" msgstr "Investeringsfrequentie"
#: templates/dca/fragments/strategy/details.html:456 #: templates/dca/fragments/strategy/details.html:455
msgid "The straighter the blue line, the more consistent your DCA strategy is." msgid "The straighter the blue line, the more consistent your DCA strategy is."
msgstr "Hoe rechter de blauwe lijn, hoe consistenter je DCA-strategie is." msgstr "Hoe rechter de blauwe lijn, hoe consistenter je DCA-strategie is."
@@ -1727,19 +1737,19 @@ msgstr "Wisselkoers bewerken"
msgid "All" msgid "All"
msgstr "Allemaal" msgstr "Allemaal"
#: templates/exchange_rates/fragments/table.html:12 #: templates/exchange_rates/fragments/table.html:11
msgid "Pairing" msgid "Pairing"
msgstr "Koppelen" msgstr "Koppelen"
#: templates/exchange_rates/fragments/table.html:13 #: templates/exchange_rates/fragments/table.html:12
msgid "Rate" msgid "Rate"
msgstr "Tarief" msgstr "Tarief"
#: templates/exchange_rates/fragments/table.html:52 #: templates/exchange_rates/fragments/table.html:51
msgid "No exchange rates" msgid "No exchange rates"
msgstr "Geen wisselkoersen" msgstr "Geen wisselkoersen"
#: templates/exchange_rates/fragments/table.html:59 #: templates/exchange_rates/fragments/table.html:58
#: templates/transactions/fragments/list_all.html:47 #: templates/transactions/fragments/list_all.html:47
msgid "Page navigation" msgid "Page navigation"
msgstr "Paginanavigatie" msgstr "Paginanavigatie"
@@ -1895,7 +1905,13 @@ msgstr "Rekenmachine"
msgid "Settings" msgid "Settings"
msgstr "Instellingen" msgstr "Instellingen"
#: templates/includes/navbar/user_menu.html:37 #: templates/includes/navbar/user_menu.html:38
#, fuzzy
#| msgid "Clear"
msgid "Clear cache"
msgstr "Leegmaken"
#: templates/includes/navbar/user_menu.html:42
msgid "Logout" msgid "Logout"
msgstr "Uitloggen" msgstr "Uitloggen"

View File

@@ -8,9 +8,9 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-01-25 16:51+0000\n" "POT-Creation-Date: 2025-01-28 00:49+0000\n"
"PO-Revision-Date: 2025-01-25 13:53-0300\n" "PO-Revision-Date: 2025-01-27 21:49-0300\n"
"Last-Translator: \n" "Last-Translator: Herculino Trotta\n"
"Language-Team: \n" "Language-Team: \n"
"Language: pt_BR\n" "Language: pt_BR\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@@ -24,28 +24,28 @@ msgid "Group name"
msgstr "Nome do grupo" msgstr "Nome do grupo"
#: apps/accounts/forms.py:40 apps/accounts/forms.py:96 #: apps/accounts/forms.py:40 apps/accounts/forms.py:96
#: apps/currencies/forms.py:52 apps/currencies/forms.py:92 apps/dca/forms.py:41 #: apps/currencies/forms.py:52 apps/currencies/forms.py:90 apps/dca/forms.py:41
#: apps/dca/forms.py:93 apps/import_app/forms.py:34 apps/rules/forms.py:45 #: apps/dca/forms.py:93 apps/import_app/forms.py:34 apps/rules/forms.py:45
#: apps/rules/forms.py:87 apps/transactions/forms.py:190 #: apps/rules/forms.py:87 apps/transactions/forms.py:190
#: apps/transactions/forms.py:257 apps/transactions/forms.py:583 #: apps/transactions/forms.py:257 apps/transactions/forms.py:581
#: apps/transactions/forms.py:626 apps/transactions/forms.py:658 #: apps/transactions/forms.py:624 apps/transactions/forms.py:656
#: apps/transactions/forms.py:693 apps/transactions/forms.py:831 #: apps/transactions/forms.py:691 apps/transactions/forms.py:827
msgid "Update" msgid "Update"
msgstr "Atualizar" msgstr "Atualizar"
#: apps/accounts/forms.py:48 apps/accounts/forms.py:104 #: apps/accounts/forms.py:48 apps/accounts/forms.py:104
#: apps/common/widgets/tom_select.py:12 apps/currencies/forms.py:60 #: apps/common/widgets/tom_select.py:12 apps/currencies/forms.py:60
#: apps/currencies/forms.py:100 apps/dca/forms.py:49 apps/dca/forms.py:102 #: apps/currencies/forms.py:98 apps/dca/forms.py:49 apps/dca/forms.py:102
#: apps/import_app/forms.py:42 apps/rules/forms.py:53 apps/rules/forms.py:95 #: apps/import_app/forms.py:42 apps/rules/forms.py:53 apps/rules/forms.py:95
#: apps/transactions/forms.py:174 apps/transactions/forms.py:199 #: apps/transactions/forms.py:174 apps/transactions/forms.py:199
#: apps/transactions/forms.py:591 apps/transactions/forms.py:634 #: apps/transactions/forms.py:589 apps/transactions/forms.py:632
#: apps/transactions/forms.py:666 apps/transactions/forms.py:701 #: apps/transactions/forms.py:664 apps/transactions/forms.py:699
#: apps/transactions/forms.py:839 #: apps/transactions/forms.py:835
#: templates/account_groups/fragments/list.html:9 #: templates/account_groups/fragments/list.html:9
#: templates/accounts/fragments/list.html:9 #: templates/accounts/fragments/list.html:9
#: templates/categories/fragments/list.html:9 #: templates/categories/fragments/list.html:9
#: templates/currencies/fragments/list.html:9 #: templates/currencies/fragments/list.html:9
#: templates/dca/fragments/strategy/details.html:38 #: templates/dca/fragments/strategy/details.html:37
#: templates/dca/fragments/strategy/list.html:9 #: templates/dca/fragments/strategy/list.html:9
#: templates/entities/fragments/list.html:9 #: templates/entities/fragments/list.html:9
#: templates/exchange_rates/fragments/list.html:10 #: templates/exchange_rates/fragments/list.html:10
@@ -69,7 +69,7 @@ msgstr "Novo saldo"
#: apps/accounts/forms.py:119 apps/rules/models.py:27 #: apps/accounts/forms.py:119 apps/rules/models.py:27
#: apps/transactions/forms.py:39 apps/transactions/forms.py:291 #: apps/transactions/forms.py:39 apps/transactions/forms.py:291
#: apps/transactions/forms.py:298 apps/transactions/forms.py:478 #: apps/transactions/forms.py:298 apps/transactions/forms.py:478
#: apps/transactions/forms.py:725 apps/transactions/models.py:159 #: apps/transactions/forms.py:723 apps/transactions/models.py:159
#: apps/transactions/models.py:311 apps/transactions/models.py:491 #: apps/transactions/models.py:311 apps/transactions/models.py:491
msgid "Category" msgid "Category"
msgstr "Categoria" msgstr "Categoria"
@@ -77,7 +77,7 @@ msgstr "Categoria"
#: apps/accounts/forms.py:126 apps/rules/models.py:28 #: apps/accounts/forms.py:126 apps/rules/models.py:28
#: apps/transactions/filters.py:74 apps/transactions/forms.py:47 #: apps/transactions/filters.py:74 apps/transactions/forms.py:47
#: apps/transactions/forms.py:307 apps/transactions/forms.py:315 #: apps/transactions/forms.py:307 apps/transactions/forms.py:315
#: apps/transactions/forms.py:471 apps/transactions/forms.py:718 #: apps/transactions/forms.py:471 apps/transactions/forms.py:716
#: apps/transactions/models.py:165 apps/transactions/models.py:313 #: apps/transactions/models.py:165 apps/transactions/models.py:313
#: apps/transactions/models.py:495 templates/includes/navbar.html:98 #: apps/transactions/models.py:495 templates/includes/navbar.html:98
#: templates/tags/fragments/list.html:5 templates/tags/pages/index.html:4 #: templates/tags/fragments/list.html:5 templates/tags/pages/index.html:4
@@ -150,7 +150,7 @@ msgstr ""
#: apps/accounts/models.py:59 apps/rules/models.py:19 #: apps/accounts/models.py:59 apps/rules/models.py:19
#: apps/transactions/forms.py:59 apps/transactions/forms.py:463 #: apps/transactions/forms.py:59 apps/transactions/forms.py:463
#: apps/transactions/forms.py:710 apps/transactions/models.py:132 #: apps/transactions/forms.py:708 apps/transactions/models.py:132
#: apps/transactions/models.py:271 apps/transactions/models.py:473 #: apps/transactions/models.py:271 apps/transactions/models.py:473
msgid "Account" msgid "Account"
msgstr "Conta" msgstr "Conta"
@@ -320,11 +320,15 @@ msgstr "Erro"
msgid "Info" msgid "Info"
msgstr "Informação" msgstr "Informação"
#: apps/common/widgets/datepicker.py:55 apps/common/widgets/datepicker.py:197 #: apps/common/views.py:110
msgid "Cache cleared successfully"
msgstr "Cache limpo com sucesso"
#: apps/common/widgets/datepicker.py:47 apps/common/widgets/datepicker.py:186
msgid "Today" msgid "Today"
msgstr "Hoje" msgstr "Hoje"
#: apps/common/widgets/datepicker.py:139 #: apps/common/widgets/datepicker.py:123
msgid "Now" msgid "Now"
msgstr "Agora" msgstr "Agora"
@@ -354,8 +358,8 @@ msgstr "Sufixo"
#: apps/currencies/forms.py:68 apps/dca/models.py:156 apps/rules/models.py:22 #: apps/currencies/forms.py:68 apps/dca/models.py:156 apps/rules/models.py:22
#: apps/transactions/forms.py:63 apps/transactions/forms.py:319 #: apps/transactions/forms.py:63 apps/transactions/forms.py:319
#: apps/transactions/models.py:142 #: apps/transactions/models.py:142
#: templates/dca/fragments/strategy/details.html:53 #: templates/dca/fragments/strategy/details.html:52
#: templates/exchange_rates/fragments/table.html:11 #: templates/exchange_rates/fragments/table.html:10
msgid "Date" msgid "Date"
msgstr "Data" msgstr "Data"
@@ -405,7 +409,7 @@ msgstr "Data e Tempo"
msgid "Exchange Rates" msgid "Exchange Rates"
msgstr "Taxas de Câmbio" msgstr "Taxas de Câmbio"
#: apps/currencies/models.py:77 #: apps/currencies/models.py:79
msgid "From and To currencies cannot be the same." msgid "From and To currencies cannot be the same."
msgstr "As moedas De e Para não podem ser as mesmas." msgstr "As moedas De e Para não podem ser as mesmas."
@@ -459,11 +463,11 @@ msgstr "Estratégias CMP"
msgid "Strategy" msgid "Strategy"
msgstr "Estratégia" msgstr "Estratégia"
#: apps/dca/models.py:158 templates/dca/fragments/strategy/details.html:55 #: apps/dca/models.py:158 templates/dca/fragments/strategy/details.html:54
msgid "Amount Paid" msgid "Amount Paid"
msgstr "Quantia paga" msgstr "Quantia paga"
#: apps/dca/models.py:161 templates/dca/fragments/strategy/details.html:54 #: apps/dca/models.py:161 templates/dca/fragments/strategy/details.html:53
msgid "Amount Received" msgid "Amount Received"
msgstr "Quantia recebida" msgstr "Quantia recebida"
@@ -641,7 +645,7 @@ msgstr "Quantia"
#: apps/rules/models.py:29 apps/transactions/filters.py:81 #: apps/rules/models.py:29 apps/transactions/filters.py:81
#: apps/transactions/forms.py:55 apps/transactions/forms.py:486 #: apps/transactions/forms.py:55 apps/transactions/forms.py:486
#: apps/transactions/forms.py:733 apps/transactions/models.py:117 #: apps/transactions/forms.py:731 apps/transactions/models.py:117
#: apps/transactions/models.py:170 apps/transactions/models.py:316 #: apps/transactions/models.py:170 apps/transactions/models.py:316
#: apps/transactions/models.py:498 templates/entities/fragments/list.html:5 #: apps/transactions/models.py:498 templates/entities/fragments/list.html:5
#: templates/entities/pages/index.html:4 templates/includes/navbar.html:100 #: templates/entities/pages/index.html:4 templates/includes/navbar.html:100
@@ -759,23 +763,23 @@ msgstr "Transferir"
msgid "From and To accounts must be different." msgid "From and To accounts must be different."
msgstr "As contas De e Para devem ser diferentes." msgstr "As contas De e Para devem ser diferentes."
#: apps/transactions/forms.py:612 #: apps/transactions/forms.py:610
msgid "Tag name" msgid "Tag name"
msgstr "Nome da Tag" msgstr "Nome da Tag"
#: apps/transactions/forms.py:644 #: apps/transactions/forms.py:642
msgid "Entity name" msgid "Entity name"
msgstr "Nome da entidade" msgstr "Nome da entidade"
#: apps/transactions/forms.py:676 #: apps/transactions/forms.py:674
msgid "Category name" msgid "Category name"
msgstr "Nome da Categoria" msgstr "Nome da Categoria"
#: apps/transactions/forms.py:678 #: apps/transactions/forms.py:676
msgid "Muted categories won't count towards your monthly total" msgid "Muted categories won't count towards your monthly total"
msgstr "As categorias silenciadas não serão contabilizadas em seu total mensal" msgstr "As categorias silenciadas não serão contabilizadas em seu total mensal"
#: apps/transactions/forms.py:850 #: apps/transactions/forms.py:846
msgid "End date should be after the start date" msgid "End date should be after the start date"
msgstr "Data final deve ser após data inicial" msgstr "Data final deve ser após data inicial"
@@ -1043,15 +1047,15 @@ msgstr "Entidade apagada com sucesso"
msgid "Installment Plan added successfully" msgid "Installment Plan added successfully"
msgstr "Parcelamento adicionado com sucesso" msgstr "Parcelamento adicionado com sucesso"
#: apps/transactions/views/installment_plans.py:117 #: apps/transactions/views/installment_plans.py:115
msgid "Installment Plan updated successfully" msgid "Installment Plan updated successfully"
msgstr "Parcelamento atualizado com sucesso" msgstr "Parcelamento atualizado com sucesso"
#: apps/transactions/views/installment_plans.py:142 #: apps/transactions/views/installment_plans.py:140
msgid "Installment Plan refreshed successfully" msgid "Installment Plan refreshed successfully"
msgstr "Parcelamento atualizado com sucesso" msgstr "Parcelamento atualizado com sucesso"
#: apps/transactions/views/installment_plans.py:160 #: apps/transactions/views/installment_plans.py:158
msgid "Installment Plan deleted successfully" msgid "Installment Plan deleted successfully"
msgstr "Parcelamento apagado com sucesso" msgstr "Parcelamento apagado com sucesso"
@@ -1059,23 +1063,23 @@ msgstr "Parcelamento apagado com sucesso"
msgid "Recurring Transaction added successfully" msgid "Recurring Transaction added successfully"
msgstr "Transação Recorrente adicionada com sucesso" msgstr "Transação Recorrente adicionada com sucesso"
#: apps/transactions/views/recurring_transactions.py:144 #: apps/transactions/views/recurring_transactions.py:142
msgid "Recurring Transaction updated successfully" msgid "Recurring Transaction updated successfully"
msgstr "Transação Recorrente atualizada com sucesso" msgstr "Transação Recorrente atualizada com sucesso"
#: apps/transactions/views/recurring_transactions.py:190 #: apps/transactions/views/recurring_transactions.py:186
msgid "Recurring transaction unpaused successfully" msgid "Recurring transaction unpaused successfully"
msgstr "Transação Recorrente despausada com sucesso" msgstr "Transação Recorrente despausada com sucesso"
#: apps/transactions/views/recurring_transactions.py:193 #: apps/transactions/views/recurring_transactions.py:189
msgid "Recurring transaction paused successfully" msgid "Recurring transaction paused successfully"
msgstr "Transação Recorrente pausada com sucesso" msgstr "Transação Recorrente pausada com sucesso"
#: apps/transactions/views/recurring_transactions.py:219 #: apps/transactions/views/recurring_transactions.py:215
msgid "Recurring transaction finished successfully" msgid "Recurring transaction finished successfully"
msgstr "Transação Recorrente finalizada com sucesso" msgstr "Transação Recorrente finalizada com sucesso"
#: apps/transactions/views/recurring_transactions.py:239 #: apps/transactions/views/recurring_transactions.py:235
msgid "Recurring Transaction deleted successfully" msgid "Recurring Transaction deleted successfully"
msgstr "Transação Recorrente apagada com sucesso" msgstr "Transação Recorrente apagada com sucesso"
@@ -1092,30 +1096,30 @@ msgid "Tag deleted successfully"
msgstr "Tag apagada com sucesso" msgstr "Tag apagada com sucesso"
#: apps/transactions/views/transactions.py:50 #: apps/transactions/views/transactions.py:50
#: apps/transactions/views/transactions.py:90 #: apps/transactions/views/transactions.py:89
msgid "Transaction added successfully" msgid "Transaction added successfully"
msgstr "Transação adicionada com sucesso" msgstr "Transação adicionada com sucesso"
#: apps/transactions/views/transactions.py:126 #: apps/transactions/views/transactions.py:123
msgid "Transaction updated successfully" msgid "Transaction updated successfully"
msgstr "Transação atualizada com sucesso" msgstr "Transação atualizada com sucesso"
#: apps/transactions/views/transactions.py:176 #: apps/transactions/views/transactions.py:173
#, python-format #, python-format
msgid "%(count)s transaction updated successfully" msgid "%(count)s transaction updated successfully"
msgid_plural "%(count)s transactions updated successfully" msgid_plural "%(count)s transactions updated successfully"
msgstr[0] "%(count)s transação atualizada com sucesso" msgstr[0] "%(count)s transação atualizada com sucesso"
msgstr[1] "%(count)s transações atualizadas com sucesso" msgstr[1] "%(count)s transações atualizadas com sucesso"
#: apps/transactions/views/transactions.py:214 #: apps/transactions/views/transactions.py:209
msgid "Transaction duplicated successfully" msgid "Transaction duplicated successfully"
msgstr "Transação duplicada com sucesso" msgstr "Transação duplicada com sucesso"
#: apps/transactions/views/transactions.py:256 #: apps/transactions/views/transactions.py:251
msgid "Transaction deleted successfully" msgid "Transaction deleted successfully"
msgstr "Transação apagada com sucesso" msgstr "Transação apagada com sucesso"
#: apps/transactions/views/transactions.py:282 #: apps/transactions/views/transactions.py:277
msgid "Transfer added successfully" msgid "Transfer added successfully"
msgstr "Transferência adicionada com sucesso" msgstr "Transferência adicionada com sucesso"
@@ -1155,21 +1159,25 @@ msgstr "E-mail ou senha inválidos"
msgid "This account is deactivated" msgid "This account is deactivated"
msgstr "Essa conta está desativada" msgstr "Essa conta está desativada"
#: apps/users/forms.py:50 apps/users/forms.py:63 #: apps/users/forms.py:50 apps/users/forms.py:63 apps/users/forms.py:85
#: templates/monthly_overview/pages/overview.html:116 #: templates/monthly_overview/pages/overview.html:116
#: templates/transactions/pages/transactions.html:35 #: templates/transactions/pages/transactions.html:35
msgid "Default" msgid "Default"
msgstr "Padrão" msgstr "Padrão"
#: apps/users/forms.py:85 apps/users/models.py:40 #: apps/users/forms.py:91 apps/users/models.py:40
msgid "Date Format" msgid "Date Format"
msgstr "Formato de Data" msgstr "Formato de Data"
#: apps/users/forms.py:90 apps/users/models.py:45 #: apps/users/forms.py:96 apps/users/models.py:45
msgid "Datetime Format" msgid "Datetime Format"
msgstr "Formato de Data e Hora" msgstr "Formato de Data e Hora"
#: apps/users/forms.py:117 #: apps/users/forms.py:102 apps/users/models.py:48
msgid "Number Format"
msgstr "Formato de Número"
#: apps/users/forms.py:131
msgid "Save" msgid "Save"
msgstr "Salvar" msgstr "Salvar"
@@ -1193,19 +1201,19 @@ msgstr "Todas as transações"
msgid "Calendar" msgid "Calendar"
msgstr "Calendário" msgstr "Calendário"
#: apps/users/models.py:50 apps/users/models.py:56 #: apps/users/models.py:53 apps/users/models.py:59
msgid "Auto" msgid "Auto"
msgstr "Automático" msgstr "Automático"
#: apps/users/models.py:52 #: apps/users/models.py:55
msgid "Language" msgid "Language"
msgstr "Linguagem" msgstr "Linguagem"
#: apps/users/models.py:58 #: apps/users/models.py:61
msgid "Time Zone" msgid "Time Zone"
msgstr "Fuso horário" msgstr "Fuso horário"
#: apps/users/models.py:64 #: apps/users/models.py:67
msgid "Start page" msgid "Start page"
msgstr "Página inicial" msgstr "Página inicial"
@@ -1241,9 +1249,9 @@ msgstr "Editar grupo de conta"
#: templates/accounts/fragments/list.html:37 #: templates/accounts/fragments/list.html:37
#: templates/categories/fragments/table.html:24 #: templates/categories/fragments/table.html:24
#: templates/currencies/fragments/list.html:33 #: templates/currencies/fragments/list.html:33
#: templates/dca/fragments/strategy/details.html:64 #: templates/dca/fragments/strategy/details.html:63
#: templates/entities/fragments/table.html:23 #: templates/entities/fragments/table.html:23
#: templates/exchange_rates/fragments/table.html:20 #: templates/exchange_rates/fragments/table.html:19
#: templates/import_app/fragments/profiles/list.html:44 #: templates/import_app/fragments/profiles/list.html:44
#: templates/installment_plans/fragments/table.html:23 #: templates/installment_plans/fragments/table.html:23
#: templates/recurring_transactions/fragments/table.html:25 #: templates/recurring_transactions/fragments/table.html:25
@@ -1255,13 +1263,13 @@ msgstr "Ações"
#: templates/account_groups/fragments/list.html:36 #: templates/account_groups/fragments/list.html:36
#: templates/accounts/fragments/list.html:41 #: templates/accounts/fragments/list.html:41
#: templates/categories/fragments/table.html:29 #: templates/categories/fragments/table.html:29
#: templates/cotton/transaction/item.html:110 #: templates/cotton/transaction/item.html:109
#: templates/cotton/ui/transactions_action_bar.html:43 #: templates/cotton/ui/transactions_action_bar.html:47
#: templates/currencies/fragments/list.html:37 #: templates/currencies/fragments/list.html:37
#: templates/dca/fragments/strategy/details.html:68 #: templates/dca/fragments/strategy/details.html:67
#: templates/dca/fragments/strategy/list.html:34 #: templates/dca/fragments/strategy/list.html:34
#: templates/entities/fragments/table.html:28 #: templates/entities/fragments/table.html:28
#: templates/exchange_rates/fragments/table.html:24 #: templates/exchange_rates/fragments/table.html:23
#: templates/import_app/fragments/profiles/list.html:48 #: templates/import_app/fragments/profiles/list.html:48
#: templates/installment_plans/fragments/table.html:27 #: templates/installment_plans/fragments/table.html:27
#: templates/recurring_transactions/fragments/table.html:29 #: templates/recurring_transactions/fragments/table.html:29
@@ -1274,13 +1282,13 @@ msgstr "Editar"
#: templates/account_groups/fragments/list.html:43 #: templates/account_groups/fragments/list.html:43
#: templates/accounts/fragments/list.html:48 #: templates/accounts/fragments/list.html:48
#: templates/categories/fragments/table.html:36 #: templates/categories/fragments/table.html:36
#: templates/cotton/transaction/item.html:125 #: templates/cotton/transaction/item.html:124
#: templates/cotton/ui/transactions_action_bar.html:80 #: templates/cotton/ui/transactions_action_bar.html:84
#: templates/currencies/fragments/list.html:44 #: templates/currencies/fragments/list.html:44
#: templates/dca/fragments/strategy/details.html:76 #: templates/dca/fragments/strategy/details.html:75
#: templates/dca/fragments/strategy/list.html:42 #: templates/dca/fragments/strategy/list.html:42
#: templates/entities/fragments/table.html:36 #: templates/entities/fragments/table.html:36
#: templates/exchange_rates/fragments/table.html:32 #: templates/exchange_rates/fragments/table.html:31
#: templates/import_app/fragments/profiles/list.html:69 #: templates/import_app/fragments/profiles/list.html:69
#: templates/import_app/fragments/runs/list.html:102 #: templates/import_app/fragments/runs/list.html:102
#: templates/installment_plans/fragments/table.html:56 #: templates/installment_plans/fragments/table.html:56
@@ -1295,13 +1303,13 @@ msgstr "Apagar"
#: templates/account_groups/fragments/list.html:47 #: templates/account_groups/fragments/list.html:47
#: templates/accounts/fragments/list.html:52 #: templates/accounts/fragments/list.html:52
#: templates/categories/fragments/table.html:41 #: templates/categories/fragments/table.html:41
#: templates/cotton/transaction/item.html:129 #: templates/cotton/transaction/item.html:128
#: templates/cotton/ui/transactions_action_bar.html:82 #: templates/cotton/ui/transactions_action_bar.html:86
#: templates/currencies/fragments/list.html:48 #: templates/currencies/fragments/list.html:48
#: templates/dca/fragments/strategy/details.html:81 #: templates/dca/fragments/strategy/details.html:80
#: templates/dca/fragments/strategy/list.html:46 #: templates/dca/fragments/strategy/list.html:46
#: templates/entities/fragments/table.html:40 #: templates/entities/fragments/table.html:40
#: templates/exchange_rates/fragments/table.html:37 #: templates/exchange_rates/fragments/table.html:36
#: templates/import_app/fragments/profiles/list.html:73 #: templates/import_app/fragments/profiles/list.html:73
#: templates/import_app/fragments/runs/list.html:106 #: templates/import_app/fragments/runs/list.html:106
#: templates/installment_plans/fragments/table.html:48 #: templates/installment_plans/fragments/table.html:48
@@ -1319,13 +1327,13 @@ msgstr "Tem certeza?"
#: templates/account_groups/fragments/list.html:48 #: templates/account_groups/fragments/list.html:48
#: templates/accounts/fragments/list.html:53 #: templates/accounts/fragments/list.html:53
#: templates/categories/fragments/table.html:42 #: templates/categories/fragments/table.html:42
#: templates/cotton/transaction/item.html:130 #: templates/cotton/transaction/item.html:129
#: templates/cotton/ui/transactions_action_bar.html:83 #: templates/cotton/ui/transactions_action_bar.html:87
#: templates/currencies/fragments/list.html:49 #: templates/currencies/fragments/list.html:49
#: templates/dca/fragments/strategy/details.html:82 #: templates/dca/fragments/strategy/details.html:81
#: templates/dca/fragments/strategy/list.html:47 #: templates/dca/fragments/strategy/list.html:47
#: templates/entities/fragments/table.html:41 #: templates/entities/fragments/table.html:41
#: templates/exchange_rates/fragments/table.html:38 #: templates/exchange_rates/fragments/table.html:37
#: templates/import_app/fragments/profiles/list.html:74 #: templates/import_app/fragments/profiles/list.html:74
#: templates/rules/fragments/list.html:49 #: templates/rules/fragments/list.html:49
#: templates/rules/fragments/transaction_rule/view.html:61 #: templates/rules/fragments/transaction_rule/view.html:61
@@ -1336,12 +1344,12 @@ msgstr "Você não será capaz de reverter isso!"
#: templates/account_groups/fragments/list.html:49 #: templates/account_groups/fragments/list.html:49
#: templates/accounts/fragments/list.html:54 #: templates/accounts/fragments/list.html:54
#: templates/categories/fragments/table.html:43 #: templates/categories/fragments/table.html:43
#: templates/cotton/transaction/item.html:131 #: templates/cotton/transaction/item.html:130
#: templates/currencies/fragments/list.html:50 #: templates/currencies/fragments/list.html:50
#: templates/dca/fragments/strategy/details.html:83 #: templates/dca/fragments/strategy/details.html:82
#: templates/dca/fragments/strategy/list.html:48 #: templates/dca/fragments/strategy/list.html:48
#: templates/entities/fragments/table.html:42 #: templates/entities/fragments/table.html:42
#: templates/exchange_rates/fragments/table.html:39 #: templates/exchange_rates/fragments/table.html:38
#: templates/import_app/fragments/profiles/list.html:75 #: templates/import_app/fragments/profiles/list.html:75
#: templates/import_app/fragments/runs/list.html:108 #: templates/import_app/fragments/runs/list.html:108
#: templates/installment_plans/fragments/table.html:62 #: templates/installment_plans/fragments/table.html:62
@@ -1416,11 +1424,11 @@ msgstr "SÁB"
msgid "SUN" msgid "SUN"
msgstr "DOM" msgstr "DOM"
#: templates/calendar_view/fragments/list_transactions.html:6 #: templates/calendar_view/fragments/list_transactions.html:5
msgid "Transactions on" msgid "Transactions on"
msgstr "Transações em" msgstr "Transações em"
#: templates/calendar_view/fragments/list_transactions.html:16 #: templates/calendar_view/fragments/list_transactions.html:15
msgid "No transactions on this date" msgid "No transactions on this date"
msgstr "Nenhuma transação nesta data" msgstr "Nenhuma transação nesta data"
@@ -1479,12 +1487,12 @@ msgstr "Fechar"
msgid "Search" msgid "Search"
msgstr "Buscar" msgstr "Buscar"
#: templates/cotton/transaction/item.html:6 #: templates/cotton/transaction/item.html:5
msgid "Select" msgid "Select"
msgstr "Selecionar" msgstr "Selecionar"
#: templates/cotton/transaction/item.html:117 #: templates/cotton/transaction/item.html:116
#: templates/cotton/ui/transactions_action_bar.html:72 #: templates/cotton/ui/transactions_action_bar.html:76
msgid "Duplicate" msgid "Duplicate"
msgstr "Duplicar" msgstr "Duplicar"
@@ -1508,62 +1516,62 @@ msgstr "Despesas Previstas"
msgid "Current Expenses" msgid "Current Expenses"
msgstr "Despesas Atuais" msgstr "Despesas Atuais"
#: templates/cotton/ui/transactions_action_bar.html:25 #: templates/cotton/ui/transactions_action_bar.html:29
msgid "Select All" msgid "Select All"
msgstr "Selecionar todos" msgstr "Selecionar todos"
#: templates/cotton/ui/transactions_action_bar.html:31 #: templates/cotton/ui/transactions_action_bar.html:35
msgid "Unselect All" msgid "Unselect All"
msgstr "Desmarcar todos" msgstr "Desmarcar todos"
#: templates/cotton/ui/transactions_action_bar.html:48 #: templates/cotton/ui/transactions_action_bar.html:52
#: templates/cotton/ui/transactions_action_bar.html:139 #: templates/cotton/ui/transactions_action_bar.html:143
msgid "Toggle Dropdown" msgid "Toggle Dropdown"
msgstr "Alternar menu suspenso" msgstr "Alternar menu suspenso"
#: templates/cotton/ui/transactions_action_bar.html:56 #: templates/cotton/ui/transactions_action_bar.html:60
msgid "Mark as unpaid" msgid "Mark as unpaid"
msgstr "Marcar como não pago" msgstr "Marcar como não pago"
#: templates/cotton/ui/transactions_action_bar.html:63 #: templates/cotton/ui/transactions_action_bar.html:67
msgid "Mark as paid" msgid "Mark as paid"
msgstr "Marcar como pago" msgstr "Marcar como pago"
#: templates/cotton/ui/transactions_action_bar.html:84 #: templates/cotton/ui/transactions_action_bar.html:88
msgid "Yes, delete them!" msgid "Yes, delete them!"
msgstr "Sim, apague!" msgstr "Sim, apague!"
#: templates/cotton/ui/transactions_action_bar.html:130 #: templates/cotton/ui/transactions_action_bar.html:134
#: templates/cotton/ui/transactions_action_bar.html:154 #: templates/cotton/ui/transactions_action_bar.html:158
#: templates/cotton/ui/transactions_action_bar.html:174 #: templates/cotton/ui/transactions_action_bar.html:178
#: templates/cotton/ui/transactions_action_bar.html:194 #: templates/cotton/ui/transactions_action_bar.html:198
#: templates/cotton/ui/transactions_action_bar.html:214 #: templates/cotton/ui/transactions_action_bar.html:218
#: templates/cotton/ui/transactions_action_bar.html:234 #: templates/cotton/ui/transactions_action_bar.html:238
#: templates/cotton/ui/transactions_action_bar.html:254 #: templates/cotton/ui/transactions_action_bar.html:258
msgid "copied!" msgid "copied!"
msgstr "copiado!" msgstr "copiado!"
#: templates/cotton/ui/transactions_action_bar.html:147 #: templates/cotton/ui/transactions_action_bar.html:151
msgid "Flat Total" msgid "Flat Total"
msgstr "Total Fixo" msgstr "Total Fixo"
#: templates/cotton/ui/transactions_action_bar.html:167 #: templates/cotton/ui/transactions_action_bar.html:171
msgid "Real Total" msgid "Real Total"
msgstr "Total Real" msgstr "Total Real"
#: templates/cotton/ui/transactions_action_bar.html:187 #: templates/cotton/ui/transactions_action_bar.html:191
msgid "Mean" msgid "Mean"
msgstr "Média" msgstr "Média"
#: templates/cotton/ui/transactions_action_bar.html:207 #: templates/cotton/ui/transactions_action_bar.html:211
msgid "Max" msgid "Max"
msgstr "Máximo" msgstr "Máximo"
#: templates/cotton/ui/transactions_action_bar.html:227 #: templates/cotton/ui/transactions_action_bar.html:231
msgid "Min" msgid "Min"
msgstr "Minímo" msgstr "Minímo"
#: templates/cotton/ui/transactions_action_bar.html:247 #: templates/cotton/ui/transactions_action_bar.html:251
msgid "Count" msgid "Count"
msgstr "Contagem" msgstr "Contagem"
@@ -1595,91 +1603,91 @@ msgstr "Editar entrada CMP"
msgid "Add DCA strategy" msgid "Add DCA strategy"
msgstr "Adicionar estratégia CMP" msgstr "Adicionar estratégia CMP"
#: templates/dca/fragments/strategy/details.html:23 #: templates/dca/fragments/strategy/details.html:22
msgid "No exchange rate available" msgid "No exchange rate available"
msgstr "Nenhuma taxa de câmbio disponível" msgstr "Nenhuma taxa de câmbio disponível"
#: templates/dca/fragments/strategy/details.html:34 #: templates/dca/fragments/strategy/details.html:33
msgid "Entries" msgid "Entries"
msgstr "Entradas" msgstr "Entradas"
#: templates/dca/fragments/strategy/details.html:56 #: templates/dca/fragments/strategy/details.html:55
msgid "Current Value" msgid "Current Value"
msgstr "Valor atual" msgstr "Valor atual"
#: templates/dca/fragments/strategy/details.html:57 #: templates/dca/fragments/strategy/details.html:56
msgid "P/L" msgid "P/L"
msgstr "P/L" msgstr "P/L"
#: templates/dca/fragments/strategy/details.html:125 #: templates/dca/fragments/strategy/details.html:124
msgid "No entries for this DCA" msgid "No entries for this DCA"
msgstr "Nenhuma entrada neste CMP" msgstr "Nenhuma entrada neste CMP"
#: templates/dca/fragments/strategy/details.html:126 #: templates/dca/fragments/strategy/details.html:125
#: templates/monthly_overview/fragments/list.html:41 #: templates/monthly_overview/fragments/list.html:41
#: templates/transactions/fragments/list_all.html:40 #: templates/transactions/fragments/list_all.html:40
msgid "Try adding one" msgid "Try adding one"
msgstr "Tente adicionar uma" msgstr "Tente adicionar uma"
#: templates/dca/fragments/strategy/details.html:136 #: templates/dca/fragments/strategy/details.html:135
msgid "Total Invested" msgid "Total Invested"
msgstr "Total investido" msgstr "Total investido"
#: templates/dca/fragments/strategy/details.html:150 #: templates/dca/fragments/strategy/details.html:149
msgid "Total Received" msgid "Total Received"
msgstr "Total recebido" msgstr "Total recebido"
#: templates/dca/fragments/strategy/details.html:164 #: templates/dca/fragments/strategy/details.html:163
msgid "Current Total Value" msgid "Current Total Value"
msgstr "Valor total atual" msgstr "Valor total atual"
#: templates/dca/fragments/strategy/details.html:178 #: templates/dca/fragments/strategy/details.html:177
msgid "Average Entry Price" msgid "Average Entry Price"
msgstr "Preço médio de entrada" msgstr "Preço médio de entrada"
#: templates/dca/fragments/strategy/details.html:192 #: templates/dca/fragments/strategy/details.html:191
msgid "Total P/L" msgid "Total P/L"
msgstr "P/L total" msgstr "P/L total"
#: templates/dca/fragments/strategy/details.html:208 #: templates/dca/fragments/strategy/details.html:207
#, python-format #, python-format
msgid "Total %% P/L" msgid "Total %% P/L"
msgstr "P/L%% Total" msgstr "P/L%% Total"
#: templates/dca/fragments/strategy/details.html:227 #: templates/dca/fragments/strategy/details.html:226
#, python-format #, python-format
msgid "P/L %%" msgid "P/L %%"
msgstr "P/L %%" msgstr "P/L %%"
#: templates/dca/fragments/strategy/details.html:289 #: templates/dca/fragments/strategy/details.html:288
msgid "Performance Over Time" msgid "Performance Over Time"
msgstr "Desempenho ao longo do tempo" msgstr "Desempenho ao longo do tempo"
#: templates/dca/fragments/strategy/details.html:307 #: templates/dca/fragments/strategy/details.html:306
msgid "Entry Price" msgid "Entry Price"
msgstr "Preço de Entrada" msgstr "Preço de Entrada"
#: templates/dca/fragments/strategy/details.html:315 #: templates/dca/fragments/strategy/details.html:314
msgid "Current Price" msgid "Current Price"
msgstr "Preço atual" msgstr "Preço atual"
#: templates/dca/fragments/strategy/details.html:323 #: templates/dca/fragments/strategy/details.html:322
msgid "Amount Bought" msgid "Amount Bought"
msgstr "Quantia comprada" msgstr "Quantia comprada"
#: templates/dca/fragments/strategy/details.html:391 #: templates/dca/fragments/strategy/details.html:390
msgid "Entry Price vs Current Price" msgid "Entry Price vs Current Price"
msgstr "Preço de Entrada vs Preço Atual" msgstr "Preço de Entrada vs Preço Atual"
#: templates/dca/fragments/strategy/details.html:407 #: templates/dca/fragments/strategy/details.html:406
msgid "Days Between Investments" msgid "Days Between Investments"
msgstr "Dias entre investimentos" msgstr "Dias entre investimentos"
#: templates/dca/fragments/strategy/details.html:454 #: templates/dca/fragments/strategy/details.html:453
msgid "Investment Frequency" msgid "Investment Frequency"
msgstr "Frequência de Investimento" msgstr "Frequência de Investimento"
#: templates/dca/fragments/strategy/details.html:456 #: templates/dca/fragments/strategy/details.html:455
msgid "The straighter the blue line, the more consistent your DCA strategy is." msgid "The straighter the blue line, the more consistent your DCA strategy is."
msgstr "" msgstr ""
"Quanto mais reta for a linha azul, mais consistente é sua estratégia de CMP." "Quanto mais reta for a linha azul, mais consistente é sua estratégia de CMP."
@@ -1725,19 +1733,19 @@ msgstr "Editar taxa de câmbio"
msgid "All" msgid "All"
msgstr "Todas" msgstr "Todas"
#: templates/exchange_rates/fragments/table.html:12 #: templates/exchange_rates/fragments/table.html:11
msgid "Pairing" msgid "Pairing"
msgstr "Pares" msgstr "Pares"
#: templates/exchange_rates/fragments/table.html:13 #: templates/exchange_rates/fragments/table.html:12
msgid "Rate" msgid "Rate"
msgstr "Taxa de Câmbio" msgstr "Taxa de Câmbio"
#: templates/exchange_rates/fragments/table.html:52 #: templates/exchange_rates/fragments/table.html:51
msgid "No exchange rates" msgid "No exchange rates"
msgstr "Nenhuma taxa de câmbio" msgstr "Nenhuma taxa de câmbio"
#: templates/exchange_rates/fragments/table.html:59 #: templates/exchange_rates/fragments/table.html:58
#: templates/transactions/fragments/list_all.html:47 #: templates/transactions/fragments/list_all.html:47
msgid "Page navigation" msgid "Page navigation"
msgstr "Navegação por página" msgstr "Navegação por página"
@@ -1894,7 +1902,11 @@ msgstr "Calculadora"
msgid "Settings" msgid "Settings"
msgstr "Configurações" msgstr "Configurações"
#: templates/includes/navbar/user_menu.html:37 #: templates/includes/navbar/user_menu.html:38
msgid "Clear cache"
msgstr "Limpar cache"
#: templates/includes/navbar/user_menu.html:42
msgid "Logout" msgid "Logout"
msgstr "Sair" msgstr "Sair"

View File

@@ -39,23 +39,23 @@
{% for transaction in date.transactions %} {% for transaction in date.transactions %}
{% if transaction.is_paid %} {% if transaction.is_paid %}
{% if transaction.type == "IN" and not transaction.account.is_asset %} {% if transaction.type == "IN" and not transaction.account.is_asset %}
<i class="fa-solid fa-circle-check tw-text-green-400" data-bs-toggle="tooltip" data-bs-title="{{ transaction.description }}"></i> <i class="fa-solid fa-circle-check tw-text-green-400" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Income' %}{% endif %}"></i>
{% elif transaction.type == "IN" and transaction.account.is_asset %} {% elif transaction.type == "IN" and transaction.account.is_asset %}
<i class="fa-solid fa-circle-check tw-text-green-300" data-bs-toggle="tooltip" data-bs-title="{{ transaction.description }}"></i> <i class="fa-solid fa-circle-check tw-text-green-300" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Income' %}{% endif %}"></i>
{% elif transaction.type == "EX" and not transaction.account.is_asset %} {% elif transaction.type == "EX" and not transaction.account.is_asset %}
<i class="fa-solid fa-circle-check tw-text-red-400" data-bs-toggle="tooltip" data-bs-title="{{ transaction.description }}"></i> <i class="fa-solid fa-circle-check tw-text-red-400" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Expense' %}{% endif %}"></i>
{% elif transaction.type == "EX" and transaction.account.is_asset %} {% elif transaction.type == "EX" and transaction.account.is_asset %}
<i class="fa-solid fa-circle-check tw-text-red-300" data-bs-toggle="tooltip" data-bs-title="{{ transaction.description }}"></i> <i class="fa-solid fa-circle-check tw-text-red-300" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Expense' %}{% endif %}"></i>
{% endif %} {% endif %}
{% else %} {% else %}
{% if transaction.type == "IN" and not transaction.account.is_asset %} {% if transaction.type == "IN" and not transaction.account.is_asset %}
<i class="fa-regular fa-circle tw-text-green-400" data-bs-toggle="tooltip" data-bs-title="{{ transaction.description }}"></i> <i class="fa-regular fa-circle tw-text-green-400" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Income' %}{% endif %}"></i>
{% elif transaction.type == "IN" and transaction.account.is_asset %} {% elif transaction.type == "IN" and transaction.account.is_asset %}
<i class="fa-regular fa-circle tw-text-green-300" data-bs-toggle="tooltip" data-bs-title="{{ transaction.description }}"></i> <i class="fa-regular fa-circle tw-text-green-300" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Income' %}{% endif %}"></i>
{% elif transaction.type == "EX" and not transaction.account.is_asset %} {% elif transaction.type == "EX" and not transaction.account.is_asset %}
<i class="fa-regular fa-circle tw-text-red-400" data-bs-toggle="tooltip" data-bs-title="{{ transaction.description }}"></i> <i class="fa-regular fa-circle tw-text-red-400" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Expense' %}{% endif %}"></i>
{% elif transaction.type == "EX" and transaction.account.is_asset %} {% elif transaction.type == "EX" and transaction.account.is_asset %}
<i class="fa-regular fa-circle tw-text-green-300" data-bs-toggle="tooltip" data-bs-title="{{ transaction.description }}"></i> <i class="fa-regular fa-circle tw-text-red-300" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Expense' %}{% endif %}"></i>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% endfor %} {% endfor %}

View File

@@ -1,19 +1,18 @@
{% extends 'extends/offcanvas.html' %} {% extends 'extends/offcanvas.html' %}
{% load date %}
{% load i18n %} {% load i18n %}
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
{% block title %}{% translate 'Transactions on' %} {{ date|custom_date:request.user }}{% endblock %} {% block title %}{% translate 'Transactions on' %} {{ date|date:"SHORT_DATE_FORMAT" }}{% endblock %}
{% block body %} {% block body %}
<div hx-get="{% url 'calendar_transactions_list' day=date.day month=date.month year=date.year %}" hx-trigger="updated from:window" hx-vals='{"disable_selection": true}' hx-target="closest .offcanvas" class="show-loading"> <div hx-get="{% url 'calendar_transactions_list' day=date.day month=date.month year=date.year %}" hx-trigger="updated from:window" hx-vals='{"disable_selection": true}' hx-target="closest .offcanvas" class="show-loading" id="transactions-list">
{% for transaction in transactions %} {% for transaction in transactions %}
<c-transaction.item <c-transaction.item :transaction="transaction"></c-transaction.item>
:transaction="transaction" {% empty %}
:disable-selection="True"></c-transaction.item> <c-msg.empty
{% empty %} title="{% translate 'No transactions on this date' %}"></c-msg.empty>
<c-msg.empty {% endfor %}
title="{% translate 'No transactions on this date' %}"></c-msg.empty> {# Floating bar #}
{% endfor %} <c-ui.transactions-action-bar></c-ui.transactions-action-bar>
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -1,4 +1,3 @@
{% load date %}
{% load i18n %} {% load i18n %}
<div class="transaction d-flex my-1 {% if transaction.type == "EX" %}expense{% else %}income{% endif %}"> <div class="transaction d-flex my-1 {% if transaction.type == "EX" %}expense{% else %}income{% endif %}">
{% if not disable_selection %} {% if not disable_selection %}
@@ -27,7 +26,7 @@
{# Date#} {# Date#}
<div class="row mb-2 mb-lg-1 tw-text-gray-400"> <div class="row mb-2 mb-lg-1 tw-text-gray-400">
<div class="col-auto pe-1"><i class="fa-solid fa-calendar fa-fw me-1 fa-xs"></i></div> <div class="col-auto pe-1"><i class="fa-solid fa-calendar fa-fw me-1 fa-xs"></i></div>
<div class="col ps-0">{{ transaction.date|custom_date:request.user }} • {{ transaction.reference_date|date:"b/Y" }}</div> <div class="col ps-0">{{ transaction.date|date:"SHORT_DATE_FORMAT" }} • {{ transaction.reference_date|date:"b/Y" }}</div>
</div> </div>
{# Description#} {# Description#}
<div class="mb-2 mb-lg-1 text-white tw-text-base"> <div class="mb-2 mb-lg-1 text-white tw-text-base">

View File

@@ -2,12 +2,16 @@
<div class="tw-sticky tw-bottom-4 tw-left-0 tw-right-0 tw-z-50 tw-hidden mx-auto tw-w-fit" id="actions-bar" <div class="tw-sticky tw-bottom-4 tw-left-0 tw-right-0 tw-z-50 tw-hidden mx-auto tw-w-fit" id="actions-bar"
_="on change from #transactions-list or htmx:afterSettle from window _="on change from #transactions-list or htmx:afterSettle from window
if no <input[type='checkbox']:checked/> in #transactions-list if no <input[type='checkbox']:checked/> in #transactions-list
add .slide-in-bottom-reverse then settle if #actions-bar
then add .tw-hidden to #actions-bar add .slide-in-bottom-reverse then settle
then remove .slide-in-bottom-reverse then add .tw-hidden to #actions-bar
then remove .slide-in-bottom-reverse
end
else else
remove .tw-hidden from #actions-bar if #actions-bar
then trigger selected_transactions_updated remove .tw-hidden from #actions-bar
then trigger selected_transactions_updated
end
end end
end"> end">
<div class="card slide-in-bottom"> <div class="card slide-in-bottom">

View File

@@ -1,4 +1,3 @@
{% load date %}
{% load currency_display %} {% load currency_display %}
{% load i18n %} {% load i18n %}
<div class="container-fluid px-md-3 py-3 column-gap-5"> <div class="container-fluid px-md-3 py-3 column-gap-5">
@@ -17,7 +16,7 @@
:prefix="strategy.payment_currency.prefix" :prefix="strategy.payment_currency.prefix"
:suffix="strategy.payment_currency.suffix" :suffix="strategy.payment_currency.suffix"
:decimal_places="strategy.payment_currency.decimal_places"> :decimal_places="strategy.payment_currency.decimal_places">
• {{ strategy.current_price.1|custom_date:request.user }} • {{ strategy.current_price.1|date:"SHORT_DATETIME_FORMAT" }}
</c-amount.display> </c-amount.display>
{% else %} {% else %}
<div class="tw-text-red-400">{% trans "No exchange rate available" %}</div> <div class="tw-text-red-400">{% trans "No exchange rate available" %}</div>
@@ -84,7 +83,7 @@
_="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a> _="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a>
</div> </div>
</td> </td>
<td>{{ entry.date|custom_date:request.user }}</td> <td>{{ entry.date|date:"SHORT_DATE_FORMAT" }}</td>
<td> <td>
<c-amount.display <c-amount.display
:amount="entry.amount_received" :amount="entry.amount_received"
@@ -222,7 +221,7 @@
new Chart(perfomancectx, { new Chart(perfomancectx, {
type: 'line', type: 'line',
data: { data: {
labels: [{% for entry in entries_data %}'{{ entry.entry.date|custom_date:request.user }}'{% if not forloop.last %}, {% endif %}{% endfor %}], labels: [{% for entry in entries_data %}'{{ entry.entry.date|date:"SHORT_DATE_FORMAT" }}'{% if not forloop.last %}, {% endif %}{% endfor %}],
datasets: [{ datasets: [{
label: '{% trans "P/L %" %}', label: '{% trans "P/L %" %}',
data: [{% for entry in entries_data %}{{ entry.profit_loss_percentage|floatformat:"-40u" }}{% if not forloop.last %}, {% endif %}{% endfor %}], data: [{% for entry in entries_data %}{{ entry.profit_loss_percentage|floatformat:"-40u" }}{% if not forloop.last %}, {% endif %}{% endfor %}],

View File

@@ -1,4 +1,3 @@
{% load date %}
{% load currency_display %} {% load currency_display %}
{% load i18n %} {% load i18n %}
<div class="card-body show-loading" hx-get="{% url 'exchange_rates_list_pair' %}" hx-trigger="updated from:window" hx-swap="outerHTML" hx-vals='{"page": "{{ page_obj.number }}", "from": "{{ from_currency|default_if_none:"" }}", "to": "{{ to_currency|default_if_none:"" }}"}'> <div class="card-body show-loading" hx-get="{% url 'exchange_rates_list_pair' %}" hx-trigger="updated from:window" hx-swap="outerHTML" hx-vals='{"page": "{{ page_obj.number }}", "from": "{{ from_currency|default_if_none:"" }}", "to": "{{ to_currency|default_if_none:"" }}"}'>
@@ -40,7 +39,7 @@
_="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a> _="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a>
</div> </div>
</td> </td>
<td class="col-3">{{ exchange_rate.date|custom_date:request.user }}</td> <td class="col-3">{{ exchange_rate.date|date:"SHORT_DATETIME_FORMAT" }}</td>
<td class="col-3"><span class="badge rounded-pill text-bg-secondary">{{ exchange_rate.from_currency.code }}</span> x <span class="badge rounded-pill text-bg-secondary">{{ exchange_rate.to_currency.code }}</span></td> <td class="col-3"><span class="badge rounded-pill text-bg-secondary">{{ exchange_rate.from_currency.code }}</span> x <span class="badge rounded-pill text-bg-secondary">{{ exchange_rate.to_currency.code }}</span></td>
<td class="col-3">1 {{ exchange_rate.from_currency.code }} ≅ {% currency_display amount=exchange_rate.rate prefix=exchange_rate.to_currency.prefix suffix=exchange_rate.to_currency.suffix decimal_places=exchange_rate.to_currency.decimal_places%}</td> <td class="col-3">1 {{ exchange_rate.from_currency.code }} ≅ {% currency_display amount=exchange_rate.rate prefix=exchange_rate.to_currency.prefix suffix=exchange_rate.to_currency.suffix decimal_places=exchange_rate.to_currency.decimal_places%}</td>
</tr> </tr>

View File

@@ -33,6 +33,11 @@
</li> </li>
{% endspaceless %} {% endspaceless %}
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
<li>
<a class="dropdown-item" hx-get="{% url 'invalidate_cache' %}" role="button">
<i class="fa-solid fa-broom me-2 fa-fw"></i>{% translate 'Clear cache' %}
</a>
</li>
<li><a class="dropdown-item" href="{% url 'logout' %}"><i class="fa-solid fa-door-open me-2 fa-fw"></i <li><a class="dropdown-item" href="{% url 'logout' %}"><i class="fa-solid fa-door-open me-2 fa-fw"></i
>{% translate 'Logout' %}</a></li> >{% translate 'Logout' %}</a></li>
</ul> </ul>

View File

@@ -5,11 +5,11 @@
{% block title %}{% translate 'Installments' %}{% endblock %} {% block title %}{% translate 'Installments' %}{% endblock %}
{% block body %} {% block body %}
<div hx-get="{% url 'installment_plan_transactions' installment_plan_id=installment_plan.id %}" hx-trigger="updated from:window" hx-vals='{"disable_selection": true}' hx-target="closest .offcanvas" class="show-loading"> <div hx-get="{% url 'installment_plan_transactions' installment_plan_id=installment_plan.id %}" hx-trigger="updated from:window" hx-vals='{"disable_selection": true}' hx-target="closest .offcanvas" class="show-loading" id="transactions-list">
{% for transaction in transactions %} {% for transaction in transactions %}
<c-transaction.item <c-transaction.item :transaction="transaction"></c-transaction.item>
:transaction="transaction" {% endfor %}
:disable-selection="True"></c-transaction.item> {# Floating bar #}
{% endfor %} <c-ui.transactions-action-bar></c-ui.transactions-action-bar>
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -23,8 +23,6 @@
<div id="content"> <div id="content">
{% block content %}{% endblock %} {% block content %}{% endblock %}
</div> </div>
{% include 'includes/toasts.html' %}
{% include 'includes/scripts.html' %} {% include 'includes/scripts.html' %}
{% block extra_js %}{% endblock %} {% block extra_js %}{% endblock %}

View File

@@ -113,9 +113,9 @@
<div class="text-sm-end" _="on change trigger updated on window"> <div class="text-sm-end" _="on change trigger updated on window">
<label for="order">{% translate "Order by" %}</label> <label for="order">{% translate "Order by" %}</label>
<select class="tw-border-0 focus-visible:tw-outline-0 w-full pe-2 tw-leading-normal text-bg-tertiary tw-font-medium rounded" name="order" id="order"> <select class="tw-border-0 focus-visible:tw-outline-0 w-full pe-2 tw-leading-normal text-bg-tertiary tw-font-medium rounded" name="order" id="order">
<option value="default">{% translate 'Default' %}</option> <option value="default" {% if order == 'default' %}selected{% endif %}>{% translate 'Default' %}</option>
<option value="older">{% translate 'Oldest first' %}</option> <option value="older" {% if order == 'older' %}selected{% endif %}>{% translate 'Oldest first' %}</option>
<option value="newer">{% translate 'Newest first' %}</option> <option value="newer" {% if order == 'newer' %}selected{% endif %}>{% translate 'Newest first' %}</option>
</select> </select>
</div> </div>
</div> </div>

View File

@@ -5,11 +5,11 @@
{% block title %}{% translate 'Transactions' %}{% endblock %} {% block title %}{% translate 'Transactions' %}{% endblock %}
{% block body %} {% block body %}
<div hx-get="{% url 'recurring_transaction_transactions' recurring_transaction_id=recurring_transaction.id %}" hx-trigger="updated from:window" hx-vals='{"disable_selection": true}' hx-target="closest .offcanvas" class="show-loading"> <div hx-get="{% url 'recurring_transaction_transactions' recurring_transaction_id=recurring_transaction.id %}" hx-trigger="updated from:window" hx-vals='{"disable_selection": true}' hx-target="closest .offcanvas" class="show-loading" id="transactions-list">
{% for transaction in transactions %} {% for transaction in transactions %}
<c-transaction.item <c-transaction.item :transaction="transaction"></c-transaction.item>
:transaction="transaction" {% endfor %}
:disable-selection="True"></c-transaction.item> {# Floating bar #}
{% endfor %} <c-ui.transactions-action-bar></c-ui.transactions-action-bar>
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -32,9 +32,9 @@
<div class="tw-content-center" _="on change trigger updated on window"> <div class="tw-content-center" _="on change trigger updated on window">
<label for="order">{% translate "Order by" %}</label> <label for="order">{% translate "Order by" %}</label>
<select class="tw-border-0 focus-visible:tw-outline-0 w-full pe-2 tw-leading-normal text-bg-tertiary tw-font-medium rounded" name="order" id="order"> <select class="tw-border-0 focus-visible:tw-outline-0 w-full pe-2 tw-leading-normal text-bg-tertiary tw-font-medium rounded" name="order" id="order">
<option value="default">{% translate 'Default' %}</option> <option value="default" {% if order == 'default' %}selected{% endif %}>{% translate 'Default' %}</option>
<option value="older">{% translate 'Oldest first' %}</option> <option value="older" {% if order == 'older' %}selected{% endif %}>{% translate 'Oldest first' %}</option>
<option value="newer">{% translate 'Newest first' %}</option> <option value="newer" {% if order == 'newer' %}selected{% endif %}>{% translate 'Newest first' %}</option>
</select> </select>
</div> </div>
</div> </div>

View File

@@ -3,13 +3,13 @@ volumes:
wygiwyh_temp: wygiwyh_temp:
services: services:
web: &django web:
build: build:
context: . context: .
dockerfile: ./docker/dev/django/Dockerfile dockerfile: ./docker/dev/django/Dockerfile
image: wygiwyh_dev_server image: wygiwyh_dev_server
container_name: wygiwyh_dev_server container_name: wygiwyh_dev_server
command: /start command: /start-supervisor
volumes: volumes:
- ./app/:/usr/src/app/:z - ./app/:/usr/src/app/:z
- ./frontend/:/usr/src/frontend:z - ./frontend/:/usr/src/frontend:z
@@ -54,12 +54,12 @@ services:
- '${SQL_PORT}:5432' - '${SQL_PORT}:5432'
restart: unless-stopped restart: unless-stopped
procrastinate: # procrastinate:
<<: *django # <<: *django
image: wygiwyh_dev_procrastinate # image: wygiwyh_dev_procrastinate
container_name: wygiwyh_dev_procrastinate # container_name: wygiwyh_dev_procrastinate
depends_on: # depends_on:
- db # - db
ports: [ ] # ports: [ ]
command: /start-procrastinate # command: /start-procrastinate
restart: unless-stopped # restart: unless-stopped

View File

@@ -2,15 +2,13 @@ services:
web: web:
image: eitchtee/wygiwyh:latest image: eitchtee/wygiwyh:latest
container_name: ${SERVER_NAME} container_name: ${SERVER_NAME}
command: /start command: /start-single
ports: ports:
- "${OUTBOUND_PORT}:8000" - "${OUTBOUND_PORT}:8000"
env_file: env_file:
- .env - .env
depends_on: depends_on:
- db - db
volumes:
- wygiwyh_temp:/usr/src/app/temp/
restart: unless-stopped restart: unless-stopped
db: db:
@@ -23,18 +21,3 @@ services:
- POSTGRES_USER=${SQL_USER} - POSTGRES_USER=${SQL_USER}
- POSTGRES_PASSWORD=${SQL_PASSWORD} - POSTGRES_PASSWORD=${SQL_PASSWORD}
- POSTGRES_DB=${SQL_DATABASE} - POSTGRES_DB=${SQL_DATABASE}
procrastinate:
image: eitchtee/wygiwyh:latest
container_name: ${PROCRASTINATE_NAME}
depends_on:
- db
env_file:
- .env
volumes:
- wygiwyh_temp:/usr/src/app/temp/
command: /start-procrastinate
restart: unless-stopped
volumes:
wygiwyh_temp:

View File

@@ -18,7 +18,7 @@ ENV PYTHONDONTWRITEBYTECODE=1 \
COPY --from=python-build-stage /usr/src/app/wheels /wheels/ COPY --from=python-build-stage /usr/src/app/wheels /wheels/
RUN apt-get update && \ RUN apt-get update && \
apt-get install --no-install-recommends -y gettext && \ apt-get install --no-install-recommends -y gettext supervisor && \
rm -rf /var/lib/apt/lists/* && \ rm -rf /var/lib/apt/lists/* && \
pip install --upgrade pip && \ pip install --upgrade pip && \
pip install --no-cache-dir --no-index --find-links=/wheels/ /wheels/* && \ pip install --no-cache-dir --no-index --find-links=/wheels/ /wheels/* && \
@@ -26,9 +26,15 @@ RUN apt-get update && \
COPY ./docker/dev/django/start /start COPY ./docker/dev/django/start /start
COPY ./docker/dev/procrastinate/start /start-procrastinate COPY ./docker/dev/procrastinate/start /start-procrastinate
COPY ./docker/dev/supervisord/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY ./docker/dev/supervisord/supervisord.conf /etc/supervisord.conf
COPY ./docker/dev/supervisord/start /start-supervisor
RUN sed -i 's/\r$//g' /start && \ RUN sed -i 's/\r$//g' /start && \
chmod +x /start && \ chmod +x /start && \
sed -i 's/\r$//g' /start-procrastinate && \ sed -i 's/\r$//g' /start-procrastinate && \
chmod +x /start-procrastinate chmod +x /start-procrastinate && \
sed -i 's/\r$//g' /start-supervisor && \
chmod +x /start-supervisor
COPY ./app . COPY ./app .

View File

@@ -0,0 +1,9 @@
#!/bin/bash
set -o errexit
set -o pipefail
set -o nounset
export TASK_WORKERS=${TASK_WORKERS:=1}
exec supervisord -c /etc/supervisor/conf.d/supervisord.conf

View File

@@ -0,0 +1,39 @@
[supervisord]
nodaemon=true
logfile=/dev/null
logfile_maxbytes=0
pidfile=/tmp/supervisord.pid
user=root
[supervisorctl]
serverurl=unix:///run/supervisord.sock
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[unix_http_server]
file=/run/supervisord.sock
chmod=0700
[program:web]
directory=/usr/src/app
command=/bin/bash /start
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/dev/fd/2
stderr_logfile_maxbytes=0
autorestart=true
startretries=5
[program:procrastinate]
directory=/usr/src/app
command=/bin/bash /start-procrastinate
process_name=%(program_name)s_%(process_num)02d
numprocs=%(ENV_TASK_WORKERS)s
numprocs_start=1
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/dev/fd/2
stderr_logfile_maxbytes=0
autorestart=true
startretries=5

View File

@@ -31,7 +31,7 @@ ENV PYTHONDONTWRITEBYTECODE=1 \
COPY --from=python-build-stage /usr/src/app/wheels /wheels/ COPY --from=python-build-stage /usr/src/app/wheels /wheels/
RUN --mount=type=cache,target=/root/.cache/apt \ RUN --mount=type=cache,target=/root/.cache/apt \
apt-get update && \ apt-get update && \
apt-get install --no-install-recommends -y gettext && \ apt-get install --no-install-recommends -y gettext supervisor && \
rm -rf /var/lib/apt/lists/* && \ rm -rf /var/lib/apt/lists/* && \
pip install --upgrade pip && \ pip install --upgrade pip && \
pip install --no-cache-dir --no-index --find-links=/wheels/ /wheels/* && \ pip install --no-cache-dir --no-index --find-links=/wheels/ /wheels/* && \
@@ -39,10 +39,15 @@ RUN --mount=type=cache,target=/root/.cache/apt \
COPY --chown=app:app ./docker/prod/django/start /start COPY --chown=app:app ./docker/prod/django/start /start
COPY --chown=app:app ./docker/prod/procrastinate/start /start-procrastinate COPY --chown=app:app ./docker/prod/procrastinate/start /start-procrastinate
COPY --chown=app:app ./docker/prod/supervisord/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY --chown=app:app ./docker/prod/supervisord/supervisord.conf /etc/supervisord.conf
COPY --chown=app:app ./docker/prod/supervisord/start /start-single
RUN sed -i 's/\r$//g' /start && \ RUN sed -i 's/\r$//g' /start && \
chmod +x /start && \ chmod +x /start && \
sed -i 's/\r$//g' /start-procrastinate && \ sed -i 's/\r$//g' /start-procrastinate && \
chmod +x /start-procrastinate chmod +x /start-procrastinate && \
sed -i 's/\r$//g' /start-single && \
chmod +x /start-single
COPY --chown=app:app ./app . COPY --chown=app:app ./app .

View File

@@ -0,0 +1,9 @@
#!/bin/bash
set -o errexit
set -o pipefail
set -o nounset
export TASK_WORKERS=${TASK_WORKERS:=1}
exec supervisord -c /etc/supervisor/conf.d/supervisord.conf

View File

@@ -0,0 +1,37 @@
[supervisord]
nodaemon=true
logfile=/dev/null
logfile_maxbytes=0
pidfile=/tmp/supervisord.pid
[supervisorctl]
serverurl=unix:///tmp/supervisord.sock
[unix_http_server]
file=/tmp/supervisord.sock
chmod=0700
[program:web]
user=app
directory=/usr/src/app
command=/bin/bash /start
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/dev/fd/2
stderr_logfile_maxbytes=0
autorestart=true
startretries=5
[program:procrastinate]
user=app
directory=/usr/src/app
command=/bin/bash /start-procrastinate
process_name=%(program_name)s_%(process_num)02d
numprocs=%(ENV_TASK_WORKERS)s
numprocs_start=1
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/dev/fd/2
stderr_logfile_maxbytes=0
autorestart=true
startretries=5

View File

@@ -163,8 +163,8 @@ window.MonthYearPicker = function createDynamicDatePicker(element) {
let opts = {...baseOpts, ...positionConfig}; let opts = {...baseOpts, ...positionConfig};
if (element.dataset.value) { if (element.dataset.value) {
opts["selectedDates"] = [element.dataset.value]; opts["selectedDates"] = [new Date(element.dataset.value + "T00:00:00")];
opts["startDate"] = [element.dataset.value]; opts["startDate"] = [new Date(element.dataset.value + "T00:00:00")];
} }
return new AirDatepicker(element, opts); return new AirDatepicker(element, opts);
}; };