Compare commits

..

52 Commits

Author SHA1 Message Date
Herculino Trotta
8de340b68b Merge pull request #190
locale(de): enable Deutsch
2025-02-24 16:34:50 -03:00
Herculino Trotta
ef15b85386 fix(locale): transactions quick search placeholder is not translatable 2025-02-24 16:34:05 -03:00
Herculino Trotta
45d939237d locale(de): enable Deutsch 2025-02-24 16:33:14 -03:00
Herculino Trotta
6bf262e514 Merge pull request #189
style(transactions): improve look on wider columns
2025-02-22 23:21:45 -03:00
Herculino Trotta
f9d9137336 style(transactions): improve look on wider columns 2025-02-22 23:21:28 -03:00
Herculino Trotta
b532521f27 Merge pull request #188 from DragonHeart69/main
update dutch to V0.11.3
2025-02-22 23:17:11 -03:00
Dimitri Decrock
1e06e2d34d update dutch to V0.11.3 2025-02-22 15:04:47 +01:00
Herculino Trotta
a33fa5e184 Merge pull request #187 from eitchtee/dev
style(transactions): improve look on wider columns
2025-02-22 01:41:27 -03:00
Herculino Trotta
a2453695d8 style(transactions): improve look on wider columns 2025-02-22 01:41:02 -03:00
Herculino Trotta
3e929d0433 Merge pull request #186
style(transactions): improve look on wider columns
2025-02-22 01:18:35 -03:00
Herculino Trotta
185fc464a5 style(transactions): improve look on wider columns 2025-02-22 01:18:20 -03:00
Herculino Trotta
647c009525 Merge pull request #185
fix(insights:latest-transactions): order transactions from newest to oldest
2025-02-22 01:02:56 -03:00
Herculino Trotta
ba75492dcc fix(insights:latest-transactions): order transactions from newest to oldest 2025-02-22 01:02:35 -03:00
Herculino Trotta
8312baaf45 Merge pull request #184
feat(tools:currency-converter): show 1:1 rates for all available currencies
2025-02-20 23:48:32 -03:00
Herculino Trotta
4d346dc278 feat(tools:currency-converter): show 1:1 rates for all available currencies 2025-02-20 23:48:08 -03:00
Herculino Trotta
70ff7fab38 Merge pull request #183 from eitchtee/dev
feat(insights): add late and recent transactions
2025-02-19 23:07:51 -03:00
Herculino Trotta
6947c6affd feat(insights): add late and recent transactions 2025-02-19 23:07:28 -03:00
Herculino Trotta
dcab83f936 Merge pull request #182
fix(insights:category-explorer): wrong sums
2025-02-19 16:02:14 -03:00
Herculino Trotta
b228e4ec26 fix(insights:category-explorer): wrong sums 2025-02-19 16:01:53 -03:00
Herculino Trotta
4071a1301f Merge pull request #181 from eitchtee/dev
fix(export): unable to import decimals
2025-02-19 15:44:50 -03:00
Herculino Trotta
5c9db10710 fix(export): unable to import decimals 2025-02-19 15:44:18 -03:00
Herculino Trotta
19c92e0014 Merge pull request #180
fix(export): 403 when exporting
2025-02-19 14:02:52 -03:00
Herculino Trotta
6459f2eb46 fix(export): 403 when exporting 2025-02-19 14:02:31 -03:00
Herculino Trotta
7926e081ef locale: update locales 2025-02-19 13:50:45 -03:00
Herculino Trotta
ceefe7075f locale: update locales 2025-02-19 13:48:54 -03:00
Herculino Trotta
ad3230fd83 Merge pull request #179 from eitchtee/export
feat: export and restore
2025-02-19 13:41:53 -03:00
Herculino Trotta
c89b07ed93 Merge branch 'main' into export 2025-02-19 13:41:04 -03:00
Herculino Trotta
201ccea842 feat: export (WIP) 2025-02-19 13:38:00 -03:00
Herculino Trotta
32ada488b4 Merge pull request #178
feat(transactions:actions): select all only selects displayed transactions
2025-02-19 09:08:06 -03:00
Herculino Trotta
794d11a355 feat(transactions:actions): select all only selects displayed transactions 2025-02-19 09:07:49 -03:00
Herculino Trotta
67f8f5fe89 Merge pull request #177
fix(transactions:actions): sum considers everything an expense
2025-02-19 09:00:02 -03:00
Herculino Trotta
9ac69fd92a fix(transactions:actions): sum considers everything an expense 2025-02-19 08:59:30 -03:00
Herculino Trotta
069f1b450c feat: export (WIP) 2025-02-19 08:51:33 -03:00
Herculino Trotta
2f388af928 Merge pull request #176
feat(insights): make sidebar sticky
2025-02-18 21:04:36 -03:00
Herculino Trotta
beeb0579ce feat(insights): make sidebar sticky 2025-02-18 21:04:09 -03:00
Herculino Trotta
a8666da57b Merge pull request #175
feat(insights:category-explorer): separate current and projected totals
2025-02-18 20:46:28 -03:00
Herculino Trotta
835316d0f3 feat(insights:category-explorer): separate current and projected totals 2025-02-18 20:46:06 -03:00
Herculino Trotta
f5feeb9617 Merge pull request #174
feat(insights:category-explorer): allow for uncategorized totals
2025-02-18 20:45:24 -03:00
Herculino Trotta
09e380a480 feat(insights:category-explorer): allow for uncategorized totals 2025-02-18 20:45:07 -03:00
Herculino Trotta
3080df9b66 feat: export (WIP) 2025-02-18 19:55:12 -03:00
Herculino Trotta
ebc41a8049 Merge pull request #173 from eitchtee/insights
fix(insights): error if filter is empty
2025-02-17 21:49:00 -03:00
Herculino Trotta
635628e30e fix(insights): error if filter is empty 2025-02-17 21:48:33 -03:00
Herculino Trotta
819a58ac06 Merge pull request #172
feat(datepicker): disable input and fix toggling dates
2025-02-17 21:37:16 -03:00
Herculino Trotta
d433375522 feat(datepicker): disable input and fix toggling dates 2025-02-17 21:36:11 -03:00
Herculino Trotta
c0150f71a8 Merge pull request #171 from eitchtee/insights
fix(insights:category-explorer): silent categories can't be displayed
2025-02-17 10:43:12 -03:00
Herculino Trotta
6119698d38 fix(insights:category-explorer): silent categories can't be displayed 2025-02-17 10:42:38 -03:00
Herculino Trotta
f5ae231601 Merge pull request #170
feat(insights:category-explorer): add empty message when there's no data or no category selected
2025-02-17 10:28:55 -03:00
Herculino Trotta
972d23abbd feat(insights:category-explorer): add empty message when there's no data or no category selected 2025-02-17 10:28:37 -03:00
Herculino Trotta
9a514a8a69 Merge pull request #169
refactor(insights:flows): improve readability when there's a lot of nodes
2025-02-17 10:21:36 -03:00
Herculino Trotta
7325231548 refactor(insights:flows): improve readability when there's a lot of nodes 2025-02-17 10:21:18 -03:00
Herculino Trotta
570657371a Merge pull request #168
fix(insights:category-explorer): use currency name instead of code
2025-02-16 19:34:15 -03:00
Herculino Trotta
67da60b5b0 fix(insights:category-explorer): use currency name instead of code 2025-02-16 19:33:58 -03:00
55 changed files with 3166 additions and 1485 deletions

View File

@@ -55,6 +55,7 @@ INSTALLED_APPS = [
"hijack",
"hijack.contrib.admin",
"django_filters",
"import_export",
"apps.users.apps.UsersConfig",
"procrastinate.contrib.django",
"apps.transactions.apps.TransactionsConfig",
@@ -63,6 +64,7 @@ INSTALLED_APPS = [
"apps.common.apps.CommonConfig",
"apps.net_worth.apps.NetWorthConfig",
"apps.import_app.apps.ImportConfig",
"apps.export_app.apps.ExportConfig",
"apps.api.apps.ApiConfig",
"cachalot",
"rest_framework",
@@ -161,6 +163,7 @@ AUTH_USER_MODEL = "users.User"
LANGUAGE_CODE = "en"
LANGUAGES = (
("de", "Deutsch"),
("en", "English"),
("nl", "Nederlands"),
("pt-br", "Português (Brasil)"),

View File

@@ -49,5 +49,6 @@ urlpatterns = [
path("", include("apps.dca.urls")),
path("", include("apps.mini_tools.urls")),
path("", include("apps.import_app.urls")),
path("", include("apps.export_app.urls")),
path("", include("apps.insights.urls")),
]

View File

@@ -19,6 +19,8 @@ class AirDatePickerInput(widgets.DateInput):
format=None,
clear_button=True,
auto_close=True,
read_only=True,
toggle_selected=None,
*args,
**kwargs,
):
@@ -26,6 +28,10 @@ class AirDatePickerInput(widgets.DateInput):
super().__init__(attrs=attrs, format=format, *args, **kwargs)
self.clear_button = clear_button
self.auto_close = auto_close
self.read_only = read_only
self.toggle_selected = (
toggle_selected if isinstance(toggle_selected, bool) else self.clear_button
)
@staticmethod
def _get_current_language():
@@ -47,9 +53,13 @@ class AirDatePickerInput(widgets.DateInput):
attrs["data-now-button-txt"] = _("Today")
attrs["data-auto-close"] = str(self.auto_close).lower()
attrs["data-clear-button"] = str(self.clear_button).lower()
attrs["data-toggle-selected"] = str(self.toggle_selected).lower()
attrs["data-language"] = self._get_current_language()
attrs["data-date-format"] = django_to_airdatepicker_datetime(self._get_format())
if self.read_only:
attrs["readonly"] = True
return attrs
def format_value(self, value):
@@ -89,6 +99,8 @@ class AirDateTimePickerInput(widgets.DateTimeInput):
timepicker=True,
clear_button=True,
auto_close=True,
read_only=True,
toggle_selected=None,
*args,
**kwargs,
):
@@ -97,6 +109,10 @@ class AirDateTimePickerInput(widgets.DateTimeInput):
self.timepicker = timepicker
self.clear_button = clear_button
self.auto_close = auto_close
self.read_only = read_only
self.toggle_selected = (
toggle_selected if isinstance(toggle_selected, bool) else self.clear_button
)
@staticmethod
def _get_current_language():
@@ -123,11 +139,15 @@ class AirDateTimePickerInput(widgets.DateTimeInput):
attrs["data-now-button-txt"] = _("Now")
attrs["data-timepicker"] = str(self.timepicker).lower()
attrs["data-auto-close"] = str(self.auto_close).lower()
attrs["data-toggle-selected"] = str(self.toggle_selected).lower()
attrs["data-clear-button"] = str(self.clear_button).lower()
attrs["data-language"] = self._get_current_language()
attrs["data-date-format"] = date_format
attrs["data-time-format"] = time_format
if self.read_only:
attrs["readonly"] = True
return attrs
def format_value(self, value):

View File

View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ExportConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "apps.export_app"

View File

@@ -0,0 +1,189 @@
from crispy_forms.bootstrap import FormActions
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, HTML
from django import forms
from django.utils.translation import gettext_lazy as _
from apps.common.widgets.crispy.submit import NoClassSubmit
class ExportForm(forms.Form):
accounts = forms.BooleanField(
required=False,
widget=forms.CheckboxInput(),
label=_("Accounts"),
initial=True,
)
currencies = forms.BooleanField(
required=False,
widget=forms.CheckboxInput(),
label=_("Currencies"),
initial=True,
)
transactions = forms.BooleanField(
required=False,
widget=forms.CheckboxInput(),
label=_("Transactions"),
initial=True,
)
categories = forms.BooleanField(
required=False,
widget=forms.CheckboxInput(),
label=_("Categories"),
initial=True,
)
tags = forms.BooleanField(
required=False,
widget=forms.CheckboxInput(),
label=_("Tags"),
initial=False,
)
entities = forms.BooleanField(
required=False,
widget=forms.CheckboxInput(),
label=_("Entities"),
initial=False,
)
recurring_transactions = forms.BooleanField(
required=False,
widget=forms.CheckboxInput(),
label=_("Recurring Transactions"),
initial=True,
)
installment_plans = forms.BooleanField(
required=False,
widget=forms.CheckboxInput(),
label=_("Installment Plans"),
initial=True,
)
exchange_rates = forms.BooleanField(
required=False,
widget=forms.CheckboxInput(),
label=_("Exchange Rates"),
initial=False,
)
exchange_rates_services = forms.BooleanField(
required=False,
widget=forms.CheckboxInput(),
label=_("Automatic Exchange Rates"),
initial=False,
)
rules = forms.BooleanField(
required=False,
widget=forms.CheckboxInput(),
label=_("Rules"),
initial=True,
)
dca = forms.BooleanField(
required=False,
widget=forms.CheckboxInput(),
label=_("DCA"),
initial=False,
)
import_profiles = forms.BooleanField(
required=False,
widget=forms.CheckboxInput(),
label=_("Import Profiles"),
initial=True,
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.form_method = "post"
self.helper.layout = Layout(
"accounts",
"currencies",
"transactions",
"categories",
"entities",
"tags",
"installment_plans",
"recurring_transactions",
"exchange_rates_services",
"exchange_rates",
"rules",
"dca",
"import_profiles",
FormActions(
NoClassSubmit(
"submit", _("Export"), css_class="btn btn-outline-primary w-100"
),
),
)
class RestoreForm(forms.Form):
zip_file = forms.FileField(
required=False,
help_text=_("Import a ZIP file exported from WYGIWYH"),
label=_("ZIP File"),
)
accounts = forms.FileField(required=False, label=_("Accounts"))
currencies = forms.FileField(required=False, label=_("Currencies"))
transactions_categories = forms.FileField(required=False, label=_("Categories"))
transactions_tags = forms.FileField(required=False, label=_("Tags"))
transactions_entities = forms.FileField(required=False, label=_("Entities"))
transactions = forms.FileField(required=False, label=_("Transactions"))
installment_plans = forms.FileField(required=False, label=_("Installment Plans"))
recurring_transactions = forms.FileField(
required=False, label=_("Recurring Transactions")
)
automatic_exchange_rates = forms.FileField(
required=False, label=_("Automatic Exchange Rates")
)
exchange_rates = forms.FileField(required=False, label=_("Exchange Rates"))
transaction_rules = forms.FileField(required=False, label=_("Transaction rules"))
transaction_rules_actions = forms.FileField(
required=False, label=_("Edit transaction action")
)
transaction_rules_update_or_create = forms.FileField(
required=False, label=_("Update or create transaction actions")
)
dca_strategies = forms.FileField(required=False, label=_("DCA Strategies"))
dca_entries = forms.FileField(required=False, label=_("DCA Entries"))
import_profiles = forms.FileField(required=False, label=_("Import Profiles"))
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.form_method = "post"
self.helper.layout = Layout(
"zip_file",
HTML("<hr />"),
"accounts",
"currencies",
"transactions",
"transactions_categories",
"transactions_entities",
"transactions_tags",
"installment_plans",
"recurring_transactions",
"automatic_exchange_rates",
"exchange_rates",
"transaction_rules",
"transaction_rules_actions",
"transaction_rules_update_or_create",
"dca_strategies",
"dca_entries",
"import_profiles",
FormActions(
NoClassSubmit(
"submit", _("Restore"), css_class="btn btn-outline-primary w-100"
),
),
)
def clean(self):
cleaned_data = super().clean()
if not cleaned_data.get("zip_file") and not any(
cleaned_data.get(field) for field in self.fields if field != "zip_file"
):
raise forms.ValidationError(
_("Please upload either a ZIP file or at least one CSV file")
)
return cleaned_data

View File

@@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View File

@@ -0,0 +1,26 @@
from import_export import fields, resources, widgets
from apps.accounts.models import Account, AccountGroup
from apps.export_app.widgets.foreign_key import AutoCreateForeignKeyWidget
from apps.currencies.models import Currency
class AccountResource(resources.ModelResource):
group = fields.Field(
attribute="group",
column_name="group",
widget=AutoCreateForeignKeyWidget(AccountGroup, "name"),
)
currency = fields.Field(
attribute="currency",
column_name="currency",
widget=widgets.ForeignKeyWidget(Currency, "name"),
)
exchange_currency = fields.Field(
attribute="exchange_currency",
column_name="exchange_currency",
widget=widgets.ForeignKeyWidget(Currency, "name"),
)
class Meta:
model = Account

View File

@@ -0,0 +1,52 @@
from import_export import fields, resources, widgets
from apps.accounts.models import Account
from apps.currencies.models import Currency, ExchangeRate, ExchangeRateService
from apps.export_app.widgets.foreign_key import SkipMissingForeignKeyWidget
from apps.export_app.widgets.numbers import UniversalDecimalWidget
class CurrencyResource(resources.ModelResource):
exchange_currency = fields.Field(
attribute="exchange_currency",
column_name="exchange_currency",
widget=SkipMissingForeignKeyWidget(Currency, "name"),
)
class Meta:
model = Currency
class ExchangeRateResource(resources.ModelResource):
from_currency = fields.Field(
attribute="from_currency",
column_name="from_currency",
widget=widgets.ForeignKeyWidget(Currency, "name"),
)
to_currency = fields.Field(
attribute="to_currency",
column_name="to_currency",
widget=widgets.ForeignKeyWidget(Currency, "name"),
)
rate = fields.Field(
attribute="rate", column_name="rate", widget=UniversalDecimalWidget()
)
class Meta:
model = ExchangeRate
class ExchangeRateServiceResource(resources.ModelResource):
target_currencies = fields.Field(
attribute="target_currencies",
column_name="target_currencies",
widget=widgets.ManyToManyWidget(Currency, field="name"),
)
target_accounts = fields.Field(
attribute="target_accounts",
column_name="target_accounts",
widget=widgets.ManyToManyWidget(Account, field="name"),
)
class Meta:
model = ExchangeRateService

View File

@@ -0,0 +1,38 @@
from import_export import fields, resources
from import_export.widgets import ForeignKeyWidget
from apps.dca.models import DCAStrategy, DCAEntry
from apps.currencies.models import Currency
from apps.export_app.widgets.numbers import UniversalDecimalWidget
class DCAStrategyResource(resources.ModelResource):
target_currency = fields.Field(
attribute="target_currency",
column_name="target_currency",
widget=ForeignKeyWidget(Currency, "name"),
)
payment_currency = fields.Field(
attribute="payment_currency",
column_name="payment_currency",
widget=ForeignKeyWidget(Currency, "name"),
)
class Meta:
model = DCAStrategy
class DCAEntryResource(resources.ModelResource):
amount_paid = fields.Field(
attribute="amount_paid",
column_name="amount_paid",
widget=UniversalDecimalWidget(),
)
amount_received = fields.Field(
attribute="amount_received",
column_name="amount_received",
widget=UniversalDecimalWidget(),
)
class Meta:
model = DCAEntry

View File

@@ -0,0 +1,8 @@
from import_export import resources
from apps.import_app.models import ImportProfile
class ImportProfileResource(resources.ModelResource):
class Meta:
model = ImportProfile

View File

@@ -0,0 +1,25 @@
from import_export import fields, resources
from import_export.widgets import ForeignKeyWidget
from apps.export_app.widgets.foreign_key import AutoCreateForeignKeyWidget
from apps.export_app.widgets.many_to_many import AutoCreateManyToManyWidget
from apps.rules.models import (
TransactionRule,
TransactionRuleAction,
UpdateOrCreateTransactionRuleAction,
)
class TransactionRuleResource(resources.ModelResource):
class Meta:
model = TransactionRule
class TransactionRuleActionResource(resources.ModelResource):
class Meta:
model = TransactionRuleAction
class UpdateOrCreateTransactionRuleResource(resources.ModelResource):
class Meta:
model = UpdateOrCreateTransactionRuleAction

View File

@@ -0,0 +1,143 @@
from import_export import fields, resources
from import_export.widgets import ForeignKeyWidget
from apps.accounts.models import Account
from apps.export_app.widgets.foreign_key import AutoCreateForeignKeyWidget
from apps.export_app.widgets.many_to_many import AutoCreateManyToManyWidget
from apps.export_app.widgets.string import EmptyStringToNoneField
from apps.transactions.models import (
Transaction,
TransactionCategory,
TransactionTag,
TransactionEntity,
RecurringTransaction,
InstallmentPlan,
)
from apps.export_app.widgets.numbers import UniversalDecimalWidget
class TransactionResource(resources.ModelResource):
account = fields.Field(
attribute="account",
column_name="account",
widget=ForeignKeyWidget(Account, "name"),
)
category = fields.Field(
attribute="category",
column_name="category",
widget=AutoCreateForeignKeyWidget(TransactionCategory, "name"),
)
tags = fields.Field(
attribute="tags",
column_name="tags",
widget=AutoCreateManyToManyWidget(TransactionTag, field="name"),
)
entities = fields.Field(
attribute="entities",
column_name="entities",
widget=AutoCreateManyToManyWidget(TransactionEntity, field="name"),
)
internal_id = EmptyStringToNoneField(
column_name="internal_id", attribute="internal_id"
)
amount = fields.Field(
attribute="amount",
column_name="amount",
widget=UniversalDecimalWidget(),
)
class Meta:
model = Transaction
def get_queryset(self):
return Transaction.all_objects.all()
class TransactionTagResource(resources.ModelResource):
class Meta:
model = TransactionTag
class TransactionEntityResource(resources.ModelResource):
class Meta:
model = TransactionEntity
class TransactionCategoyResource(resources.ModelResource):
class Meta:
model = TransactionCategory
class RecurringTransactionResource(resources.ModelResource):
account = fields.Field(
attribute="account",
column_name="account",
widget=ForeignKeyWidget(Account, "name"),
)
category = fields.Field(
attribute="category",
column_name="category",
widget=AutoCreateForeignKeyWidget(TransactionCategory, "name"),
)
tags = fields.Field(
attribute="tags",
column_name="tags",
widget=AutoCreateManyToManyWidget(TransactionTag, field="name"),
)
entities = fields.Field(
attribute="entities",
column_name="entities",
widget=AutoCreateManyToManyWidget(TransactionEntity, field="name"),
)
amount = fields.Field(
attribute="amount",
column_name="amount",
widget=UniversalDecimalWidget(),
)
class Meta:
model = RecurringTransaction
class InstallmentPlanResource(resources.ModelResource):
account = fields.Field(
attribute="account",
column_name="account",
widget=ForeignKeyWidget(Account, "name"),
)
category = fields.Field(
attribute="category",
column_name="category",
widget=AutoCreateForeignKeyWidget(TransactionCategory, "name"),
)
tags = fields.Field(
attribute="tags",
column_name="tags",
widget=AutoCreateManyToManyWidget(TransactionTag, field="name"),
)
entities = fields.Field(
attribute="entities",
column_name="entities",
widget=AutoCreateManyToManyWidget(TransactionEntity, field="name"),
)
installment_amount = fields.Field(
attribute="installment_amount",
column_name="installment_amount",
widget=UniversalDecimalWidget(),
)
class Meta:
model = InstallmentPlan

View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@@ -0,0 +1,8 @@
from django.urls import path
import apps.export_app.views as views
urlpatterns = [
path("export/", views.export_index, name="export_index"),
path("export/form/", views.export_form, name="export_form"),
path("export/restore/", views.import_form, name="restore_form"),
]

View File

@@ -0,0 +1,284 @@
import logging
import zipfile
from io import BytesIO, TextIOWrapper
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.db import transaction
from django.http import HttpResponse
from django.shortcuts import render
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.views.decorators.http import require_http_methods
from tablib import Dataset
from apps.export_app.forms import ExportForm, RestoreForm
from apps.export_app.resources.accounts import AccountResource
from apps.export_app.resources.transactions import (
TransactionResource,
TransactionTagResource,
TransactionEntityResource,
TransactionCategoyResource,
InstallmentPlanResource,
RecurringTransactionResource,
)
from apps.export_app.resources.currencies import (
CurrencyResource,
ExchangeRateResource,
ExchangeRateServiceResource,
)
from apps.export_app.resources.rules import (
TransactionRuleResource,
TransactionRuleActionResource,
UpdateOrCreateTransactionRuleResource,
)
from apps.export_app.resources.dca import (
DCAStrategyResource,
DCAEntryResource,
)
from apps.export_app.resources.import_app import (
ImportProfileResource,
)
from apps.common.decorators.htmx import only_htmx
logger = logging.getLogger()
@login_required
@require_http_methods(["GET"])
def export_index(request):
return render(request, "export_app/pages/index.html")
@login_required
@require_http_methods(["GET", "POST"])
def export_form(request):
timestamp = timezone.localtime(timezone.now()).strftime("%Y-%m-%dT%H-%M-%S")
if request.method == "POST":
form = ExportForm(request.POST)
if form.is_valid():
zip_buffer = BytesIO()
export_accounts = form.cleaned_data.get("accounts", False)
export_currencies = form.cleaned_data.get("currencies", False)
export_transactions = form.cleaned_data.get("transactions", False)
export_categories = form.cleaned_data.get("categories", False)
export_tags = form.cleaned_data.get("tags", False)
export_entities = form.cleaned_data.get("entities", False)
export_installment_plans = form.cleaned_data.get("installment_plans", False)
export_recurring_transactions = form.cleaned_data.get(
"recurring_transactions", False
)
export_exchange_rates_services = form.cleaned_data.get(
"exchange_rates_services", False
)
export_exchange_rates = form.cleaned_data.get("exchange_rates", False)
export_rules = form.cleaned_data.get("rules", False)
export_dca = form.cleaned_data.get("dca", False)
export_import_profiles = form.cleaned_data.get("import_profiles", False)
exports = []
if export_accounts:
exports.append((AccountResource().export(), "accounts"))
if export_currencies:
exports.append((CurrencyResource().export(), "currencies"))
if export_transactions:
exports.append((TransactionResource().export(), "transactions"))
if export_categories:
exports.append(
(TransactionCategoyResource().export(), "transactions_categories")
)
if export_tags:
exports.append((TransactionTagResource().export(), "transactions_tags"))
if export_entities:
exports.append(
(TransactionEntityResource().export(), "transactions_entities")
)
if export_installment_plans:
exports.append(
(InstallmentPlanResource().export(), "installment_plans")
)
if export_recurring_transactions:
exports.append(
(RecurringTransactionResource().export(), "recurring_transactions")
)
if export_exchange_rates_services:
exports.append(
(ExchangeRateServiceResource().export(), "automatic_exchange_rates")
)
if export_exchange_rates:
exports.append((ExchangeRateResource().export(), "exchange_rates"))
if export_rules:
exports.append(
(TransactionRuleResource().export(), "transaction_rules")
)
exports.append(
(
TransactionRuleActionResource().export(),
"transaction_rules_actions",
)
)
exports.append(
(
UpdateOrCreateTransactionRuleResource().export(),
"transaction_rules_update_or_create",
)
)
if export_dca:
exports.append((DCAStrategyResource().export(), "dca_strategies"))
exports.append(
(
DCAEntryResource().export(),
"dca_entries",
)
)
if export_import_profiles:
exports.append((ImportProfileResource().export(), "import_profiles"))
if len(exports) >= 2:
with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zip_file:
for dataset, name in exports:
zip_file.writestr(f"{name}.csv", dataset.csv)
response = HttpResponse(
zip_buffer.getvalue(),
content_type="application/zip",
headers={
"HX-Trigger": "hide_offcanvas, updated",
"Content-Disposition": f'attachment; filename="{timestamp}_WYGIWYH_export.zip"',
},
)
return response
elif len(exports) == 1:
dataset, name = exports[0]
response = HttpResponse(
dataset.csv,
content_type="text/csv",
headers={
"HX-Trigger": "hide_offcanvas, updated",
"Content-Disposition": f'attachment; filename="{timestamp}_WYGIWYH_export_{name}.csv"',
},
)
return response
else:
return HttpResponse(
_("You have to select at least one export"),
)
else:
form = ExportForm()
return render(request, "export_app/fragments/export.html", context={"form": form})
@only_htmx
@login_required
@require_http_methods(["GET", "POST"])
def import_form(request):
if request.method == "POST":
form = RestoreForm(request.POST, request.FILES)
if form.is_valid():
try:
process_imports(request, form.cleaned_data)
messages.success(request, _("Data restored successfully"))
return HttpResponse(
status=204,
headers={
"HX-Trigger": "hide_offcanvas, updated",
},
)
except Exception as e:
logger.error("Error importing", exc_info=e)
messages.error(
request,
_(
"There was an error restoring your data. Check the logs for more details."
),
)
else:
form = RestoreForm()
response = render(request, "export_app/fragments/restore.html", {"form": form})
response["HX-Trigger"] = "updated"
return response
def process_imports(request, cleaned_data):
# Define import order to handle dependencies
import_order = [
("currencies", CurrencyResource),
(
"currencies",
CurrencyResource,
), # We do a double pass because exchange_currency may not exist when currency is initially created
("accounts", AccountResource),
("transactions_categories", TransactionCategoyResource),
("transactions_tags", TransactionTagResource),
("transactions_entities", TransactionEntityResource),
("automatic_exchange_rates", ExchangeRateServiceResource),
("exchange_rates", ExchangeRateResource),
("installment_plans", InstallmentPlanResource),
("recurring_transactions", RecurringTransactionResource),
("transactions", TransactionResource),
("dca_strategies", DCAStrategyResource),
("dca_entries", DCAEntryResource),
("import_profiles", ImportProfileResource),
("transaction_rules", TransactionRuleResource),
("transaction_rules_actions", TransactionRuleActionResource),
("transaction_rules_update_or_create", UpdateOrCreateTransactionRuleResource),
]
def import_dataset(content, resource_class, field_name):
try:
# Create a new resource instance
resource = resource_class()
# Create dataset from CSV content
dataset = Dataset()
dataset.load(content, format="csv")
# Perform the import
result = resource.import_data(
dataset,
dry_run=False,
raise_errors=True,
collect_failed_rows=True,
use_transactions=False,
skip_unchanged=True,
)
if result.has_errors():
raise ImportError(f"Failed rows: {result.failed_dataset}")
return result
except Exception as e:
logger.error(f"Error importing {field_name}: {str(e)}")
raise ImportError(f"Error importing {field_name}: {str(e)}")
with transaction.atomic():
files = {}
if zip_file := cleaned_data.get("zip_file"):
# Process ZIP file
with zipfile.ZipFile(zip_file) as z:
for filename in z.namelist():
name = filename.replace(".csv", "")
with z.open(filename) as f:
content = f.read().decode("utf-8")
files[name] = content
for field_name, resource_class in import_order:
if field_name in files.keys():
content = files[field_name]
import_dataset(content, resource_class, field_name)
else:
# Process individual files
for field_name, resource_class in import_order:
if csv_file := cleaned_data.get(field_name):
content = csv_file.read().decode("utf-8")
import_dataset(content, resource_class, field_name)

View File

View File

@@ -0,0 +1,22 @@
from import_export.widgets import ForeignKeyWidget
class AutoCreateForeignKeyWidget(ForeignKeyWidget):
def clean(self, value, row=None, *args, **kwargs):
if value:
try:
return super().clean(value, row, **kwargs)
except self.model.DoesNotExist:
return self.model.objects.create(name=value)
return None
class SkipMissingForeignKeyWidget(ForeignKeyWidget):
def clean(self, value, row=None, *args, **kwargs):
if not value:
return None
try:
return super().clean(value, row, *args, **kwargs)
except self.model.DoesNotExist:
return None

View File

@@ -0,0 +1,21 @@
from import_export.widgets import ManyToManyWidget
class AutoCreateManyToManyWidget(ManyToManyWidget):
def clean(self, value, row=None, *args, **kwargs):
if not value:
return []
values = value.split(self.separator)
cleaned_values = []
for val in values:
val = val.strip()
if val:
try:
obj = self.model.objects.get(**{self.field: val})
except self.model.DoesNotExist:
obj = self.model.objects.create(name=val)
cleaned_values.append(obj)
return cleaned_values

View File

@@ -0,0 +1,18 @@
from decimal import Decimal
from import_export.widgets import NumberWidget
class UniversalDecimalWidget(NumberWidget):
def clean(self, value, row=None, *args, **kwargs):
if self.is_empty(value):
return None
# Replace comma with dot if present
if isinstance(value, str):
value = value.replace(",", ".")
return Decimal(str(value))
def render(self, value, obj=None, **kwargs):
if value is None:
return ""
return str(value).replace(",", ".")

View File

@@ -0,0 +1,7 @@
from import_export import fields
class EmptyStringToNoneField(fields.Field):
def clean(self, data, **kwargs):
value = super().clean(data)
return None if value == "" else value

View File

@@ -9,11 +9,12 @@ from apps.common.widgets.datepicker import (
AirDatePickerInput,
)
from apps.transactions.models import TransactionCategory
from apps.common.widgets.tom_select import TomSelect
class SingleMonthForm(forms.Form):
month = forms.DateField(
widget=AirMonthYearPickerInput(clear_button=False), label="", required=False
widget=AirMonthYearPickerInput(clear_button=False), label="", required=True
)
def __init__(self, *args, **kwargs):
@@ -28,7 +29,7 @@ class SingleMonthForm(forms.Form):
class SingleYearForm(forms.Form):
year = forms.DateField(
widget=AirYearPickerInput(clear_button=False), label="", required=False
widget=AirYearPickerInput(clear_button=False), label="", required=True
)
def __init__(self, *args, **kwargs):
@@ -43,10 +44,10 @@ class SingleYearForm(forms.Form):
class MonthRangeForm(forms.Form):
month_from = forms.DateField(
widget=AirMonthYearPickerInput(clear_button=False), label="", required=False
widget=AirMonthYearPickerInput(clear_button=False), label="", required=True
)
month_to = forms.DateField(
widget=AirMonthYearPickerInput(clear_button=False), label="", required=False
widget=AirMonthYearPickerInput(clear_button=False), label="", required=True
)
def __init__(self, *args, **kwargs):
@@ -66,10 +67,10 @@ class MonthRangeForm(forms.Form):
class YearRangeForm(forms.Form):
year_from = forms.DateField(
widget=AirYearPickerInput(clear_button=False), label="", required=False
widget=AirYearPickerInput(clear_button=False), label="", required=True
)
year_to = forms.DateField(
widget=AirYearPickerInput(clear_button=False), label="", required=False
widget=AirYearPickerInput(clear_button=False), label="", required=True
)
def __init__(self, *args, **kwargs):
@@ -89,10 +90,10 @@ class YearRangeForm(forms.Form):
class DateRangeForm(forms.Form):
date_from = forms.DateField(
widget=AirDatePickerInput(clear_button=False), label="", required=False
widget=AirDatePickerInput(clear_button=False), label="", required=True
)
date_to = forms.DateField(
widget=AirDatePickerInput(clear_button=False), label="", required=False
widget=AirDatePickerInput(clear_button=False), label="", required=True
)
def __init__(self, *args, **kwargs):
@@ -115,7 +116,9 @@ class CategoryForm(forms.Form):
category = forms.ModelChoiceField(
required=False,
label=_("Category"),
empty_label=_("Uncategorized"),
queryset=TransactionCategory.objects.filter(active=True),
widget=TomSelect(clear_button=True),
)
def __init__(self, *args, **kwargs):

View File

@@ -29,4 +29,14 @@ urlpatterns = [
views.category_sum_by_currency,
name="category_sum_by_currency",
),
path(
"insights/late-transactions/",
views.late_transactions,
name="insights_late_transactions",
),
path(
"insights/latest-transactions/",
views.latest_transactions,
name="insights_latest_transactions",
),
]

View File

@@ -3,7 +3,7 @@ from django.db.models.functions import Coalesce
from django.utils.translation import gettext_lazy as _
def get_category_sums_by_account(queryset, category):
def get_category_sums_by_account(queryset, category=None):
"""
Returns income/expense sums per account for a specific category.
"""
@@ -11,10 +11,10 @@ def get_category_sums_by_account(queryset, category):
queryset.filter(category=category)
.values("account__name")
.annotate(
income=Coalesce(
current_income=Coalesce(
Sum(
Case(
When(type="IN", then="amount"),
When(type="IN", is_paid=True, then="amount"),
default=Value(0),
output_field=DecimalField(max_digits=42, decimal_places=30),
)
@@ -22,10 +22,32 @@ def get_category_sums_by_account(queryset, category):
Value(0),
output_field=DecimalField(max_digits=42, decimal_places=30),
),
expense=Coalesce(
current_expense=Coalesce(
Sum(
Case(
When(type="EX", then=-F("amount")),
When(type="EX", is_paid=True, then=-F("amount")),
default=Value(0),
output_field=DecimalField(max_digits=42, decimal_places=30),
)
),
Value(0),
output_field=DecimalField(max_digits=42, decimal_places=30),
),
projected_income=Coalesce(
Sum(
Case(
When(type="IN", is_paid=False, then="amount"),
default=Value(0),
output_field=DecimalField(max_digits=42, decimal_places=30),
)
),
Value(0),
output_field=DecimalField(max_digits=42, decimal_places=30),
),
projected_expense=Coalesce(
Sum(
Case(
When(type="EX", is_paid=False, then=-F("amount")),
default=Value(0),
output_field=DecimalField(max_digits=42, decimal_places=30),
)
@@ -41,29 +63,37 @@ def get_category_sums_by_account(queryset, category):
"labels": [item["account__name"] for item in sums],
"datasets": [
{
"label": _("Income"),
"data": [float(item["income"]) for item in sums],
"label": _("Current Income"),
"data": [float(item["current_income"]) for item in sums],
},
{
"label": _("Expenses"),
"data": [float(item["expense"]) for item in sums],
"label": _("Current Expenses"),
"data": [float(item["current_expense"]) for item in sums],
},
{
"label": _("Projected Income"),
"data": [float(item["projected_income"]) for item in sums],
},
{
"label": _("Projected Expenses"),
"data": [float(item["projected_expense"]) for item in sums],
},
],
}
def get_category_sums_by_currency(queryset, category):
def get_category_sums_by_currency(queryset, category=None):
"""
Returns income/expense sums per currency for a specific category.
"""
sums = (
queryset.filter(category=category)
.values("account__currency__code")
.values("account__currency__name")
.annotate(
income=Coalesce(
current_income=Coalesce(
Sum(
Case(
When(type="IN", then="amount"),
When(type="IN", is_paid=True, then="amount"),
default=Value(0),
output_field=DecimalField(max_digits=42, decimal_places=30),
)
@@ -71,10 +101,32 @@ def get_category_sums_by_currency(queryset, category):
Value(0),
output_field=DecimalField(max_digits=42, decimal_places=30),
),
expense=Coalesce(
current_expense=Coalesce(
Sum(
Case(
When(type="EX", then=-F("amount")),
When(type="EX", is_paid=True, then=-F("amount")),
default=Value(0),
output_field=DecimalField(max_digits=42, decimal_places=30),
)
),
Value(0),
output_field=DecimalField(max_digits=42, decimal_places=30),
),
projected_income=Coalesce(
Sum(
Case(
When(type="IN", is_paid=False, then="amount"),
default=Value(0),
output_field=DecimalField(max_digits=42, decimal_places=30),
)
),
Value(0),
output_field=DecimalField(max_digits=42, decimal_places=30),
),
projected_expense=Coalesce(
Sum(
Case(
When(type="EX", is_paid=False, then=-F("amount")),
default=Value(0),
output_field=DecimalField(max_digits=42, decimal_places=30),
)
@@ -83,19 +135,27 @@ def get_category_sums_by_currency(queryset, category):
output_field=DecimalField(max_digits=42, decimal_places=30),
),
)
.order_by("account__currency__code")
.order_by("account__currency__name")
)
return {
"labels": [item["account__currency__code"] for item in sums],
"labels": [item["account__currency__name"] for item in sums],
"datasets": [
{
"label": _("Income"),
"data": [float(item["income"]) for item in sums],
"label": _("Current Income"),
"data": [float(item["current_income"]) for item in sums],
},
{
"label": _("Expenses"),
"data": [float(item["expense"]) for item in sums],
"label": _("Current Expenses"),
"data": [float(item["current_expense"]) for item in sums],
},
{
"label": _("Projected Income"),
"data": [float(item["projected_income"]) for item in sums],
},
{
"label": _("Projected Expenses"),
"data": [float(item["projected_expense"]) for item in sums],
},
],
}

View File

@@ -22,7 +22,7 @@ from apps.insights.utils.sankey import (
generate_sankey_data_by_currency,
)
from apps.insights.utils.transactions import get_transactions
from apps.transactions.models import TransactionCategory
from apps.transactions.models import TransactionCategory, Transaction
@login_required
@@ -116,7 +116,7 @@ def category_explorer_index(request):
@require_http_methods(["GET"])
def category_sum_by_account(request):
# Get filtered transactions
transactions = get_transactions(request)
transactions = get_transactions(request, include_silent=True)
category = request.GET.get("category")
@@ -126,7 +126,7 @@ def category_sum_by_account(request):
# Generate data
account_data = get_category_sums_by_account(transactions, category)
else:
account_data = None
account_data = get_category_sums_by_account(transactions, category=None)
return render(
request,
@@ -140,7 +140,7 @@ def category_sum_by_account(request):
@require_http_methods(["GET"])
def category_sum_by_currency(request):
# Get filtered transactions
transactions = get_transactions(request)
transactions = get_transactions(request, include_silent=True)
category = request.GET.get("category")
@@ -150,12 +150,40 @@ def category_sum_by_currency(request):
# Generate data
currency_data = get_category_sums_by_currency(transactions, category)
else:
currency_data = None
print(currency_data)
currency_data = get_category_sums_by_currency(transactions, category=None)
return render(
request,
"insights/fragments/category_explorer/charts/currency.html",
{"currency_data": currency_data},
)
@only_htmx
@login_required
@require_http_methods(["GET"])
def latest_transactions(request):
limit = timezone.now() - relativedelta(days=3)
transactions = Transaction.objects.filter(created_at__gte=limit).order_by("-id")[
:30
]
return render(
request,
"insights/fragments/latest_transactions.html",
{"transactions": transactions},
)
@only_htmx
@login_required
@require_http_methods(["GET"])
def late_transactions(request):
now = timezone.localdate(timezone.now())
transactions = Transaction.objects.filter(is_paid=False, date__lt=now)
return render(
request,
"insights/fragments/late_transactions.html",
{"transactions": transactions},
)

View File

View File

@@ -0,0 +1,85 @@
from typing import Dict
from django.db.models import Func, F, Value
from django.db.models.functions import Extract
from django.utils import timezone
from apps.currencies.models import ExchangeRate
def get_currency_exchange_map(date=None) -> Dict[str, dict]:
"""
Creates a nested dictionary of exchange rates and currency information.
Returns:
{
'BTC': {
'decimal_places': 8,
'prefix': '',
'suffix': '',
'rates': {'USD': Decimal('34000.00'), 'EUR': Decimal('31000.00')}
},
'USD': {
'decimal_places': 2,
'prefix': '$',
'suffix': '',
'rates': {'BTC': Decimal('0.0000294'), 'EUR': Decimal('0.91')}
},
...
}
"""
if date is None:
date = timezone.localtime(timezone.now())
# Get all exchange rates for the closest date
exchange_rates = (
ExchangeRate.objects.select_related(
"from_currency", "to_currency"
) # Optimize currency queries
.annotate(
date_diff=Func(Extract(F("date") - Value(date), "epoch"), function="ABS"),
effective_rate=F("rate"),
)
.order_by("from_currency", "to_currency", "date_diff")
.distinct("from_currency", "to_currency")
)
# Initialize the result dictionary
rate_map = {}
# Build the exchange rate mapping with currency info
for rate in exchange_rates:
# Add from_currency info if not exists
if rate.from_currency.name not in rate_map:
rate_map[rate.from_currency.name] = {
"decimal_places": rate.from_currency.decimal_places,
"prefix": rate.from_currency.prefix,
"suffix": rate.from_currency.suffix,
"rates": {},
}
# Add to_currency info if not exists
if rate.to_currency.name not in rate_map:
rate_map[rate.to_currency.name] = {
"decimal_places": rate.to_currency.decimal_places,
"prefix": rate.to_currency.prefix,
"suffix": rate.to_currency.suffix,
"rates": {},
}
# Add direct rate
rate_map[rate.from_currency.name]["rates"][rate.to_currency.name] = {
"rate": rate.rate,
"decimal_places": rate.to_currency.decimal_places,
"prefix": rate.to_currency.prefix,
"suffix": rate.to_currency.suffix,
}
# Add inverse rate
rate_map[rate.to_currency.name]["rates"][rate.from_currency.name] = {
"rate": 1 / rate.rate,
"decimal_places": rate.from_currency.decimal_places,
"prefix": rate.from_currency.prefix,
"suffix": rate.from_currency.suffix,
}
return rate_map

View File

@@ -5,6 +5,7 @@ from apps.common.widgets.decimal import convert_to_decimal
from apps.currencies.models import Currency
from apps.currencies.utils.convert import convert
from apps.mini_tools.forms import CurrencyConverterForm
from apps.mini_tools.utils.exchange_rate_map import get_currency_exchange_map
@login_required
@@ -14,11 +15,13 @@ def unit_price_calculator(request):
@login_required
def currency_converter(request):
rate_map = get_currency_exchange_map()
form = CurrencyConverterForm()
return render(
request,
"mini_tools/currency_converter/currency_converter.html",
context={"form": form},
context={"form": form, "rate_map": rate_map},
)

View File

@@ -63,7 +63,9 @@ class TransactionForm(forms.ModelForm):
date = forms.DateField(label=_("Date"))
reference_date = forms.DateField(
widget=AirMonthYearPickerInput(), label=_("Reference Date"), required=False
widget=AirMonthYearPickerInput(),
label=_("Reference Date"),
required=False,
)
class Meta:
@@ -176,7 +178,6 @@ class TransactionForm(forms.ModelForm):
),
)
self.fields["reference_date"].required = False
self.fields["date"].widget = AirDatePickerInput(clear_button=False)
if self.instance and self.instance.pk:

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.6 on 2025-02-24 19:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0018_alter_usersettings_start_page'),
]
operations = [
migrations.AlterField(
model_name='usersettings',
name='language',
field=models.CharField(choices=[('auto', 'Auto'), ('de', 'Deutsch'), ('en', 'English'), ('nl', 'Nederlands'), ('pt-br', 'Português (Brasil)')], default='auto', max_length=10, verbose_name='Language'),
),
]

File diff suppressed because it is too large Load Diff

View File

@@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-02-16 13:04-0300\n"
"PO-Revision-Date: 2025-02-12 06:58+0100\n"
"POT-Creation-Date: 2025-02-24 16:30-0300\n"
"PO-Revision-Date: 2025-02-22 15:03+0100\n"
"Last-Translator: Dimitri Decrock <dimitri@fam-decrock.eu>\n"
"Language-Team: \n"
"Language: nl\n"
@@ -27,10 +27,10 @@ msgstr "Groepsnaam"
#: apps/currencies/forms.py:53 apps/currencies/forms.py:91
#: apps/currencies/forms.py:142 apps/dca/forms.py:49 apps/dca/forms.py:224
#: apps/import_app/forms.py:34 apps/rules/forms.py:45 apps/rules/forms.py:87
#: apps/rules/forms.py:359 apps/transactions/forms.py:190
#: apps/transactions/forms.py:257 apps/transactions/forms.py:581
#: apps/transactions/forms.py:624 apps/transactions/forms.py:656
#: apps/transactions/forms.py:691 apps/transactions/forms.py:827
#: apps/rules/forms.py:359 apps/transactions/forms.py:191
#: apps/transactions/forms.py:258 apps/transactions/forms.py:582
#: apps/transactions/forms.py:625 apps/transactions/forms.py:657
#: apps/transactions/forms.py:692 apps/transactions/forms.py:828
msgid "Update"
msgstr "Bijwerken"
@@ -39,10 +39,10 @@ msgstr "Bijwerken"
#: apps/currencies/forms.py:99 apps/currencies/forms.py:150
#: apps/dca/forms.py:57 apps/dca/forms.py:232 apps/import_app/forms.py:42
#: apps/rules/forms.py:53 apps/rules/forms.py:95 apps/rules/forms.py:367
#: apps/transactions/forms.py:174 apps/transactions/forms.py:199
#: apps/transactions/forms.py:589 apps/transactions/forms.py:632
#: apps/transactions/forms.py:664 apps/transactions/forms.py:699
#: apps/transactions/forms.py:835
#: apps/transactions/forms.py:176 apps/transactions/forms.py:200
#: apps/transactions/forms.py:590 apps/transactions/forms.py:633
#: apps/transactions/forms.py:665 apps/transactions/forms.py:700
#: apps/transactions/forms.py:836
#: templates/account_groups/fragments/list.html:9
#: templates/accounts/fragments/list.html:9
#: templates/categories/fragments/list.html:9
@@ -70,21 +70,22 @@ msgid "New balance"
msgstr "Nieuw saldo"
#: apps/accounts/forms.py:119 apps/dca/forms.py:85 apps/dca/forms.py:92
#: apps/insights/forms.py:117 apps/rules/forms.py:168 apps/rules/forms.py:183
#: apps/insights/forms.py:118 apps/rules/forms.py:168 apps/rules/forms.py:183
#: apps/rules/models.py:32 apps/rules/models.py:280
#: apps/transactions/forms.py:39 apps/transactions/forms.py:291
#: apps/transactions/forms.py:298 apps/transactions/forms.py:478
#: apps/transactions/forms.py:723 apps/transactions/models.py:203
#: apps/transactions/forms.py:39 apps/transactions/forms.py:292
#: apps/transactions/forms.py:299 apps/transactions/forms.py:479
#: apps/transactions/forms.py:724 apps/transactions/models.py:203
#: apps/transactions/models.py:378 apps/transactions/models.py:558
msgid "Category"
msgstr "Categorie"
#: apps/accounts/forms.py:126 apps/dca/forms.py:101 apps/dca/forms.py:109
#: apps/export_app/forms.py:38 apps/export_app/forms.py:127
#: apps/rules/forms.py:171 apps/rules/forms.py:180 apps/rules/models.py:33
#: apps/rules/models.py:284 apps/transactions/filters.py:74
#: apps/transactions/forms.py:47 apps/transactions/forms.py:307
#: apps/transactions/forms.py:315 apps/transactions/forms.py:471
#: apps/transactions/forms.py:716 apps/transactions/models.py:209
#: apps/transactions/forms.py:47 apps/transactions/forms.py:308
#: apps/transactions/forms.py:316 apps/transactions/forms.py:472
#: apps/transactions/forms.py:717 apps/transactions/models.py:209
#: apps/transactions/models.py:380 apps/transactions/models.py:562
#: templates/includes/navbar.html:108 templates/tags/fragments/list.html:5
#: templates/tags/pages/index.html:4
@@ -159,13 +160,14 @@ msgstr ""
#: apps/accounts/models.py:59 apps/rules/forms.py:160 apps/rules/forms.py:173
#: apps/rules/models.py:24 apps/rules/models.py:236
#: apps/transactions/forms.py:59 apps/transactions/forms.py:463
#: apps/transactions/forms.py:708 apps/transactions/models.py:176
#: apps/transactions/forms.py:59 apps/transactions/forms.py:464
#: apps/transactions/forms.py:709 apps/transactions/models.py:176
#: apps/transactions/models.py:338 apps/transactions/models.py:540
msgid "Account"
msgstr "Rekening"
#: apps/accounts/models.py:60 apps/transactions/filters.py:53
#: apps/accounts/models.py:60 apps/export_app/forms.py:14
#: apps/export_app/forms.py:124 apps/transactions/filters.py:53
#: templates/accounts/fragments/list.html:5
#: templates/accounts/pages/index.html:4 templates/includes/navbar.html:114
#: templates/includes/navbar.html:116
@@ -337,12 +339,12 @@ msgstr "Info"
msgid "Cache cleared successfully"
msgstr "Categorie succesvol bijgewerkt"
#: apps/common/widgets/datepicker.py:47 apps/common/widgets/datepicker.py:186
#: apps/common/widgets/datepicker.py:244
#: apps/common/widgets/datepicker.py:53 apps/common/widgets/datepicker.py:206
#: apps/common/widgets/datepicker.py:264
msgid "Today"
msgstr "Vandaag"
#: apps/common/widgets/datepicker.py:123
#: apps/common/widgets/datepicker.py:139
msgid "Now"
msgstr "Nu"
@@ -371,7 +373,7 @@ msgstr "Achtervoegsel"
#: apps/currencies/forms.py:69 apps/dca/models.py:156 apps/rules/forms.py:163
#: apps/rules/forms.py:176 apps/rules/models.py:27 apps/rules/models.py:248
#: apps/transactions/forms.py:63 apps/transactions/forms.py:319
#: apps/transactions/forms.py:63 apps/transactions/forms.py:320
#: apps/transactions/models.py:186
#: templates/dca/fragments/strategy/details.html:52
#: templates/exchange_rates/fragments/table.html:10
@@ -391,7 +393,8 @@ msgstr "Munteenheids Naam"
msgid "Decimal Places"
msgstr "Cijfers na de komma"
#: apps/currencies/models.py:40 apps/transactions/filters.py:60
#: apps/currencies/models.py:40 apps/export_app/forms.py:20
#: apps/export_app/forms.py:125 apps/transactions/filters.py:60
#: templates/currencies/fragments/list.html:5
#: templates/currencies/pages/index.html:4 templates/includes/navbar.html:122
#: templates/includes/navbar.html:124
@@ -421,7 +424,8 @@ msgstr "Wisselkoers"
msgid "Date and Time"
msgstr "Datum en Tijd"
#: apps/currencies/models.py:74 templates/exchange_rates/fragments/list.html:6
#: apps/currencies/models.py:74 apps/export_app/forms.py:62
#: apps/export_app/forms.py:137 templates/exchange_rates/fragments/list.html:6
#: templates/exchange_rates/pages/index.html:4
#: templates/includes/navbar.html:126
msgid "Exchange Rates"
@@ -578,16 +582,14 @@ msgid "Services queued successfully"
msgstr "Diensten succesvol in de wachtrij geplaatst"
#: apps/dca/forms.py:65 apps/dca/forms.py:164
#, fuzzy
#| msgid "Deleted transactions"
msgid "Create transaction"
msgstr "Verwijderde verrichtingen"
msgstr "Maak verrichtingen"
#: apps/dca/forms.py:70 apps/transactions/forms.py:266
#: apps/dca/forms.py:70 apps/transactions/forms.py:267
msgid "From Account"
msgstr "Van rekening"
#: apps/dca/forms.py:76 apps/transactions/forms.py:271
#: apps/dca/forms.py:76 apps/transactions/forms.py:272
msgid "To Account"
msgstr "Naar rekening"
@@ -598,31 +600,29 @@ msgstr "Uitgave Transactie"
#: apps/dca/forms.py:120 apps/dca/forms.py:130
msgid "Type to search for a transaction to link to this entry"
msgstr ""
"Type om een transactie te zoeken die aan dit item moet worden gekoppeld"
#: apps/dca/forms.py:126 apps/dca/models.py:177
msgid "Income Transaction"
msgstr "Ontvangsten Transactie"
#: apps/dca/forms.py:210
#, fuzzy
#| msgid "Edit transaction"
msgid "Link transaction"
msgstr "Bewerk verrichting"
msgstr "Koppel verrichting"
#: apps/dca/forms.py:279 apps/dca/forms.py:280 apps/dca/forms.py:285
#: apps/dca/forms.py:289
msgid "You must provide an account."
msgstr ""
msgstr "Je moet een account opgeven."
#: apps/dca/forms.py:294 apps/transactions/forms.py:413
#: apps/dca/forms.py:294 apps/transactions/forms.py:414
msgid "From and To accounts must be different."
msgstr "Van en Naar rekening moeten verschillend zijn."
#: apps/dca/forms.py:308
#, fuzzy, python-format
#| msgid "DCA Strategies"
#, python-format
msgid "DCA for %(strategy_name)s"
msgstr "DCA Strategieën"
msgstr "DCA voor %(strategy_name)s"
#: apps/dca/models.py:17
msgid "Target Currency"
@@ -634,7 +634,7 @@ msgstr "Betaal Munteenheid"
#: apps/dca/models.py:27 apps/dca/models.py:179 apps/rules/forms.py:167
#: apps/rules/forms.py:182 apps/rules/models.py:31 apps/rules/models.py:264
#: apps/transactions/forms.py:333 apps/transactions/models.py:199
#: apps/transactions/forms.py:334 apps/transactions/models.py:199
#: apps/transactions/models.py:387 apps/transactions/models.py:568
msgid "Notes"
msgstr "Opmerkingen"
@@ -643,7 +643,7 @@ msgstr "Opmerkingen"
msgid "DCA Strategy"
msgstr "DCA Strategie"
#: apps/dca/models.py:33
#: apps/dca/models.py:33 apps/export_app/forms.py:145
msgid "DCA Strategies"
msgstr "DCA Strategieën"
@@ -663,7 +663,7 @@ msgstr "Ontvangen bedrag"
msgid "DCA Entry"
msgstr "DCA Instap"
#: apps/dca/models.py:185
#: apps/dca/models.py:185 apps/export_app/forms.py:146
msgid "DCA Entries"
msgstr "DCA Idems"
@@ -691,6 +691,119 @@ msgstr "Item succesvol bijgewerkt"
msgid "Entry deleted successfully"
msgstr "Item succesvol verwijderd"
#: apps/export_app/forms.py:26 apps/export_app/forms.py:129
#: apps/transactions/models.py:256 templates/includes/navbar.html:57
#: templates/includes/navbar.html:104
#: templates/recurring_transactions/fragments/list_transactions.html:5
#: templates/recurring_transactions/fragments/table.html:37
#: templates/transactions/pages/transactions.html:5
msgid "Transactions"
msgstr "Verrichtingen"
#: apps/export_app/forms.py:32 apps/export_app/forms.py:126
#: apps/transactions/filters.py:67 templates/categories/fragments/list.html:5
#: templates/categories/pages/index.html:4 templates/includes/navbar.html:106
msgid "Categories"
msgstr "Categorieën"
#: apps/export_app/forms.py:44 apps/export_app/forms.py:128
#: apps/rules/forms.py:172 apps/rules/forms.py:181 apps/rules/models.py:34
#: apps/rules/models.py:276 apps/transactions/filters.py:81
#: apps/transactions/forms.py:55 apps/transactions/forms.py:487
#: apps/transactions/forms.py:732 apps/transactions/models.py:161
#: apps/transactions/models.py:214 apps/transactions/models.py:383
#: apps/transactions/models.py:565 templates/entities/fragments/list.html:5
#: templates/entities/pages/index.html:4 templates/includes/navbar.html:110
msgid "Entities"
msgstr "Bedrijven"
#: apps/export_app/forms.py:50 apps/export_app/forms.py:132
#: apps/transactions/models.py:592 templates/includes/navbar.html:74
#: templates/recurring_transactions/fragments/list.html:5
#: templates/recurring_transactions/pages/index.html:4
msgid "Recurring Transactions"
msgstr "Terugkerende Verrichtingen"
#: apps/export_app/forms.py:56 apps/export_app/forms.py:130
#: apps/transactions/models.py:391 templates/includes/navbar.html:72
#: templates/installment_plans/fragments/list.html:5
#: templates/installment_plans/pages/index.html:4
msgid "Installment Plans"
msgstr "Afbetalingsplannen"
#: apps/export_app/forms.py:68 apps/export_app/forms.py:135
#: templates/exchange_rates_services/fragments/list.html:6
#: templates/exchange_rates_services/pages/index.html:4
#: templates/includes/navbar.html:138
msgid "Automatic Exchange Rates"
msgstr "Automatische Wisselkoersen"
#: apps/export_app/forms.py:74 templates/includes/navbar.html:132
#: templates/rules/fragments/list.html:5 templates/rules/pages/index.html:4
msgid "Rules"
msgstr "Regels"
#: apps/export_app/forms.py:80 templates/cotton/transaction/item.html:56
msgid "DCA"
msgstr "DCA"
#: apps/export_app/forms.py:86 apps/export_app/forms.py:147
#: templates/import_app/fragments/profiles/list.html:5
#: templates/import_app/pages/profiles_index.html:4
msgid "Import Profiles"
msgstr "Profielen importeren"
#: apps/export_app/forms.py:112 templates/export_app/fragments/export.html:5
#: templates/export_app/pages/index.html:15
msgid "Export"
msgstr "Exporteer"
#: apps/export_app/forms.py:121
msgid "Import a ZIP file exported from WYGIWYH"
msgstr "Importeer een ZIP-bestand geëxporteerd vanuit WYGIWYH"
#: apps/export_app/forms.py:122
msgid "ZIP File"
msgstr "ZIP-bestand"
#: apps/export_app/forms.py:138 apps/rules/models.py:16
msgid "Transaction rules"
msgstr "Verrichtingsregels"
#: apps/export_app/forms.py:140 apps/rules/models.py:53
msgid "Edit transaction action"
msgstr "Bewerk verrichtingsregel actie"
#: apps/export_app/forms.py:143 apps/rules/models.py:290
msgid "Update or create transaction actions"
msgstr "Bewerk of maak verrichtingsregel acties"
#: apps/export_app/forms.py:176 templates/cotton/transaction/item.html:158
#: templates/cotton/ui/deleted_transactions_action_bar.html:47
#: templates/export_app/fragments/restore.html:5
#: templates/export_app/pages/index.html:24
msgid "Restore"
msgstr "Herstel"
#: apps/export_app/forms.py:187
msgid "Please upload either a ZIP file or at least one CSV file"
msgstr "Upload een ZIP-bestand of ten minste één CSV-bestand"
#: apps/export_app/views.py:168
msgid "You have to select at least one export"
msgstr "U moet ten minste één export selecteren"
#: apps/export_app/views.py:186
msgid "Data restored successfully"
msgstr "Gegevens succesvol hersteld"
#: apps/export_app/views.py:198
msgid ""
"There was an error restoring your data. Check the logs for more details."
msgstr ""
"Er is een fout opgetreden bij het herstellen van uw gegevens. Controleer de "
"logboeken voor meer details."
#: apps/import_app/forms.py:49
msgid "Select a file"
msgstr "Selecteer een bestand"
@@ -765,39 +878,51 @@ msgstr "Importrun met succes in de wachtrij geplaatst"
msgid "Run deleted successfully"
msgstr "Run met succes verwijderd"
#: apps/insights/utils/category_explorer.py:44
#: apps/insights/utils/category_explorer.py:93 apps/transactions/models.py:170
#: templates/calendar_view/fragments/list.html:42
#: templates/calendar_view/fragments/list.html:44
#: templates/calendar_view/fragments/list.html:52
#: templates/calendar_view/fragments/list.html:54
#: templates/cotton/ui/quick_transactions_buttons.html:10
#: templates/insights/fragments/category_explorer/charts/account.html:54
#: templates/insights/fragments/category_explorer/charts/currency.html:55
#: templates/monthly_overview/fragments/monthly_summary.html:39
msgid "Income"
msgstr "Ontvangsten Transactie"
#: apps/insights/utils/category_explorer.py:48
#: apps/insights/utils/category_explorer.py:97
#: templates/insights/fragments/category_explorer/charts/account.html:60
#: templates/insights/fragments/category_explorer/charts/currency.html:61
#: templates/monthly_overview/fragments/monthly_summary.html:103
msgid "Expenses"
msgstr "Uitgaven"
#: apps/insights/utils/sankey.py:36 apps/insights/utils/sankey.py:167
#, fuzzy
#| msgid "Categories"
#: apps/insights/forms.py:119 apps/insights/utils/sankey.py:36
#: apps/insights/utils/sankey.py:167
msgid "Uncategorized"
msgstr "Categorieën"
msgstr "Ongecategoriseerd"
#: apps/insights/utils/category_explorer.py:66
#: apps/insights/utils/category_explorer.py:145
#: templates/cotton/ui/percentage_distribution.html:10
#: templates/cotton/ui/percentage_distribution.html:14
#: templates/insights/fragments/category_explorer/charts/account.html:72
#: templates/insights/fragments/category_explorer/charts/currency.html:72
msgid "Current Income"
msgstr "Huidige inkomsten"
#: apps/insights/utils/category_explorer.py:70
#: apps/insights/utils/category_explorer.py:149
#: templates/cotton/ui/percentage_distribution.html:24
#: templates/cotton/ui/percentage_distribution.html:28
#: templates/insights/fragments/category_explorer/charts/account.html:66
#: templates/insights/fragments/category_explorer/charts/currency.html:66
msgid "Current Expenses"
msgstr "Huidige uitgaven"
#: apps/insights/utils/category_explorer.py:74
#: apps/insights/utils/category_explorer.py:153
#: templates/cotton/ui/percentage_distribution.html:3
#: templates/cotton/ui/percentage_distribution.html:7
#: templates/insights/fragments/category_explorer/charts/account.html:78
#: templates/insights/fragments/category_explorer/charts/currency.html:78
msgid "Projected Income"
msgstr "Verwachte inkomsten"
#: apps/insights/utils/category_explorer.py:78
#: apps/insights/utils/category_explorer.py:157
#: templates/cotton/ui/percentage_distribution.html:17
#: templates/cotton/ui/percentage_distribution.html:21
#: templates/insights/fragments/category_explorer/charts/account.html:60
#: templates/insights/fragments/category_explorer/charts/currency.html:60
msgid "Projected Expenses"
msgstr "Verwachte uitgaven"
#: apps/insights/utils/sankey.py:133 apps/insights/utils/sankey.py:134
#: apps/insights/utils/sankey.py:263 apps/insights/utils/sankey.py:264
#, fuzzy
#| msgid "Save"
msgid "Saved"
msgstr "Opslaan"
msgstr "Opgeslagen"
#: apps/rules/forms.py:20
msgid "Run on creation"
@@ -815,7 +940,7 @@ msgstr "Als..."
msgid "Set field"
msgstr "Veld instellen"
#: apps/rules/forms.py:65 templates/insights/fragments/sankey.html:90
#: apps/rules/forms.py:65 templates/insights/fragments/sankey.html:94
msgid "To"
msgstr "Naar"
@@ -847,8 +972,8 @@ msgid "Paid"
msgstr "Betaald"
#: apps/rules/forms.py:164 apps/rules/forms.py:177 apps/rules/models.py:28
#: apps/rules/models.py:252 apps/transactions/forms.py:66
#: apps/transactions/forms.py:322 apps/transactions/forms.py:492
#: apps/rules/models.py:252 apps/transactions/forms.py:67
#: apps/transactions/forms.py:323 apps/transactions/forms.py:493
#: apps/transactions/models.py:187 apps/transactions/models.py:361
#: apps/transactions/models.py:570
msgid "Reference Date"
@@ -856,13 +981,13 @@ msgstr "Referentiedatum"
#: apps/rules/forms.py:165 apps/rules/forms.py:178 apps/rules/models.py:29
#: apps/rules/models.py:256 apps/transactions/models.py:192
#: apps/transactions/models.py:551 templates/insights/fragments/sankey.html:91
#: apps/transactions/models.py:551 templates/insights/fragments/sankey.html:95
msgid "Amount"
msgstr "Bedrag"
#: apps/rules/forms.py:166 apps/rules/forms.py:179 apps/rules/models.py:11
#: apps/rules/models.py:30 apps/rules/models.py:260
#: apps/transactions/forms.py:325 apps/transactions/models.py:197
#: apps/transactions/forms.py:326 apps/transactions/models.py:197
#: apps/transactions/models.py:345 apps/transactions/models.py:554
msgid "Description"
msgstr "Beschrijving"
@@ -877,16 +1002,6 @@ msgstr "Interne opmerking"
msgid "Internal ID"
msgstr "Interne ID"
#: apps/rules/forms.py:172 apps/rules/forms.py:181 apps/rules/models.py:34
#: apps/rules/models.py:276 apps/transactions/filters.py:81
#: apps/transactions/forms.py:55 apps/transactions/forms.py:486
#: apps/transactions/forms.py:731 apps/transactions/models.py:161
#: apps/transactions/models.py:214 apps/transactions/models.py:383
#: apps/transactions/models.py:565 templates/entities/fragments/list.html:5
#: templates/entities/pages/index.html:4 templates/includes/navbar.html:110
msgid "Entities"
msgstr "Bedrijven"
#: apps/rules/forms.py:199
msgid "Search Criteria"
msgstr "Zoek Vereisten"
@@ -903,10 +1018,6 @@ msgstr "Trigger"
msgid "Transaction rule"
msgstr "Verrichtingsregel"
#: apps/rules/models.py:16
msgid "Transaction rules"
msgstr "Verrichtingsregels"
#: apps/rules/models.py:40 apps/rules/models.py:78
msgid "Rule"
msgstr "Regel"
@@ -919,10 +1030,6 @@ msgstr "Veld"
msgid "Value"
msgstr "Waarde"
#: apps/rules/models.py:53
msgid "Edit transaction action"
msgstr "Bewerk verrichtingsregel actie"
#: apps/rules/models.py:54
msgid "Edit transaction actions"
msgstr "Bewerk verrichtingsregel acties"
@@ -968,22 +1075,17 @@ msgid "Filter"
msgstr "Filter"
#: apps/rules/models.py:85
#, fuzzy
msgid ""
"Generic expression to enable or disable execution. Should evaluate to True "
"or False"
msgstr ""
"Generieke expressie om uitvoering in of uit te schakelen. Moet evalueren "
"naar True of False"
"naar Waar of Onwaar"
#: apps/rules/models.py:289
msgid "Update or create transaction action"
msgstr "Bewerk of maak verrichtingsregel actie"
#: apps/rules/models.py:290
msgid "Update or create transaction actions"
msgstr "Bewerk of maak verrichtingsregel acties"
#: apps/rules/views.py:52
msgid "Rule deactivated successfully"
msgstr "Regel succesvol uitgeschakeld"
@@ -1039,11 +1141,6 @@ msgstr "Inhoud"
msgid "Transaction Type"
msgstr "Soort transactie"
#: apps/transactions/filters.py:67 templates/categories/fragments/list.html:5
#: templates/categories/pages/index.html:4 templates/includes/navbar.html:106
msgid "Categories"
msgstr "Categorieën"
#: apps/transactions/filters.py:91
msgid "Date from"
msgstr "Datum vanaf"
@@ -1064,40 +1161,40 @@ msgstr "Minimum bedrag"
msgid "Amount max"
msgstr "Maximaal bedrag"
#: apps/transactions/forms.py:158
#: apps/transactions/forms.py:160
msgid "More"
msgstr "Meer"
#: apps/transactions/forms.py:278
#: apps/transactions/forms.py:279
msgid "From Amount"
msgstr "Van Bedrag"
#: apps/transactions/forms.py:283
#: apps/transactions/forms.py:284
msgid "To Amount"
msgstr "Naar Bedrag"
#: apps/transactions/forms.py:398
#: apps/transactions/forms.py:399
#: templates/cotton/ui/quick_transactions_buttons.html:40
msgid "Transfer"
msgstr "Overschrijving"
#: apps/transactions/forms.py:610
#: apps/transactions/forms.py:611
msgid "Tag name"
msgstr "Labelnaam"
#: apps/transactions/forms.py:642
#: apps/transactions/forms.py:643
msgid "Entity name"
msgstr "Naam van bedrijf"
#: apps/transactions/forms.py:674
#: apps/transactions/forms.py:675
msgid "Category name"
msgstr "Naam van categorie"
#: apps/transactions/forms.py:676
#: apps/transactions/forms.py:677
msgid "Muted categories won't count towards your monthly total"
msgstr "Gedempte categorieën tellen niet mee voor je maandtotaal"
#: apps/transactions/forms.py:846
#: apps/transactions/forms.py:847
msgid "End date should be after the start date"
msgstr "De einddatum moet na de begindatum vallen"
@@ -1144,6 +1241,16 @@ msgstr ""
msgid "Entity"
msgstr "Bedrijf"
#: apps/transactions/models.py:170
#: templates/calendar_view/fragments/list.html:42
#: templates/calendar_view/fragments/list.html:44
#: templates/calendar_view/fragments/list.html:52
#: templates/calendar_view/fragments/list.html:54
#: templates/cotton/ui/quick_transactions_buttons.html:10
#: templates/monthly_overview/fragments/monthly_summary.html:39
msgid "Income"
msgstr "Ontvangsten Transactie"
#: apps/transactions/models.py:171
#: templates/calendar_view/fragments/list.html:46
#: templates/calendar_view/fragments/list.html:48
@@ -1173,29 +1280,17 @@ msgstr "Verwijderd Op"
msgid "Transaction"
msgstr "Verrichting"
#: apps/transactions/models.py:256 templates/includes/navbar.html:57
#: templates/includes/navbar.html:104
#: templates/recurring_transactions/fragments/list_transactions.html:5
#: templates/recurring_transactions/fragments/table.html:37
#: templates/transactions/pages/transactions.html:5
msgid "Transactions"
msgstr "Verrichtingen"
#: apps/transactions/models.py:323 templates/tags/fragments/table.html:53
msgid "No tags"
msgstr "Geen labels"
#: apps/transactions/models.py:324
#, fuzzy
#| msgid "No categories"
msgid "No category"
msgstr "Geen categorieën"
msgstr "Geen categorie"
#: apps/transactions/models.py:326
#, fuzzy
#| msgid "Description"
msgid "No description"
msgstr "Beschrijving"
msgstr "Geen Beschrijving"
#: apps/transactions/models.py:332
msgid "Yearly"
@@ -1242,12 +1337,6 @@ msgstr "Terugkeerpatroon"
msgid "Installment Amount"
msgstr "Termijnbedrag"
#: apps/transactions/models.py:391 templates/includes/navbar.html:72
#: templates/installment_plans/fragments/list.html:5
#: templates/installment_plans/pages/index.html:4
msgid "Installment Plans"
msgstr "Afbetalingsplannen"
#: apps/transactions/models.py:533
msgid "day(s)"
msgstr "dag(en)"
@@ -1285,12 +1374,6 @@ msgstr "Laatste Gegenereerde Datum"
msgid "Last Generated Reference Date"
msgstr "Laatste Gegenereerde Referentiedatum"
#: apps/transactions/models.py:592 templates/includes/navbar.html:74
#: templates/recurring_transactions/fragments/list.html:5
#: templates/recurring_transactions/pages/index.html:4
msgid "Recurring Transactions"
msgstr "Terugkerende Verrichtingen"
#: apps/transactions/validators.py:8
#, python-format
msgid "%(value)s has too many decimal places. Maximum is 30."
@@ -1812,6 +1895,7 @@ msgstr "Sluiten"
#: templates/cotton/config/search.html:6
#: templates/import_app/fragments/profiles/list_presets.html:13
#: templates/monthly_overview/pages/overview.html:177
msgid "Search"
msgstr "Zoeken"
@@ -1819,20 +1903,11 @@ msgstr "Zoeken"
msgid "Select"
msgstr "Selecteer"
#: templates/cotton/transaction/item.html:56
msgid "DCA"
msgstr ""
#: templates/cotton/transaction/item.html:137
#: templates/cotton/ui/transactions_action_bar.html:78
msgid "Duplicate"
msgstr "Dupliceren"
#: templates/cotton/transaction/item.html:158
#: templates/cotton/ui/deleted_transactions_action_bar.html:47
msgid "Restore"
msgstr "Herstel"
#: templates/cotton/ui/account_card.html:15
#: templates/cotton/ui/currency_card.html:10
msgid "projected income"
@@ -1936,26 +2011,6 @@ msgstr "Minimaal"
msgid "Count"
msgstr "Rekenen"
#: templates/cotton/ui/percentage_distribution.html:3
#: templates/cotton/ui/percentage_distribution.html:7
msgid "Projected Income"
msgstr "Verwachte inkomsten"
#: templates/cotton/ui/percentage_distribution.html:10
#: templates/cotton/ui/percentage_distribution.html:14
msgid "Current Income"
msgstr "Huidige inkomsten"
#: templates/cotton/ui/percentage_distribution.html:17
#: templates/cotton/ui/percentage_distribution.html:21
msgid "Projected Expenses"
msgstr "Verwachte uitgaven"
#: templates/cotton/ui/percentage_distribution.html:24
#: templates/cotton/ui/percentage_distribution.html:28
msgid "Current Expenses"
msgstr "Huidige uitgaven"
#: templates/cotton/ui/quick_transactions_buttons.html:25
msgid "Installment"
msgstr "Afbetaling"
@@ -2156,12 +2211,6 @@ msgstr "Geen wisselkoersen"
msgid "Page navigation"
msgstr "Paginanavigatie"
#: templates/exchange_rates_services/fragments/list.html:6
#: templates/exchange_rates_services/pages/index.html:4
#: templates/includes/navbar.html:136
msgid "Automatic Exchange Rates"
msgstr "Automatische Wisselkoersen"
#: templates/exchange_rates_services/fragments/list.html:21
msgid "Fetch all"
msgstr "Alles Ophalen"
@@ -2190,6 +2239,10 @@ msgstr "rekeningen"
msgid "No services configured"
msgstr "Geen diensten ingesteld"
#: templates/export_app/pages/index.html:4 templates/includes/navbar.html:136
msgid "Export and Restore"
msgstr "Exporteren en Herstellen"
#: templates/import_app/fragments/profiles/add.html:6
msgid "Add new import profile"
msgstr "Nieuw importprofiel toevoegen"
@@ -2202,11 +2255,6 @@ msgstr "Een bericht van de auteur"
msgid "Edit import profile"
msgstr "Importprofiel bewerken"
#: templates/import_app/fragments/profiles/list.html:5
#: templates/import_app/pages/profiles_index.html:4
msgid "Import Profiles"
msgstr "Profielen importeren"
#: templates/import_app/fragments/profiles/list.html:17
msgid "New"
msgstr "Nieuw"
@@ -2298,7 +2346,7 @@ msgstr "Huidige"
#: templates/includes/navbar.html:50
msgid "Insights"
msgstr ""
msgstr "Inzichten"
#: templates/includes/navbar.html:66
msgid "Trash Can"
@@ -2332,20 +2380,15 @@ msgstr "Beheer"
msgid "Automation"
msgstr "Automatisatie"
#: templates/includes/navbar.html:132 templates/rules/fragments/list.html:5
#: templates/rules/pages/index.html:4
msgid "Rules"
msgstr "Regels"
#: templates/includes/navbar.html:146
#: templates/includes/navbar.html:148
msgid "Only use this if you know what you're doing"
msgstr "Gebruik dit alleen als je weet wat je doet"
#: templates/includes/navbar.html:147
#: templates/includes/navbar.html:149
msgid "Django Admin"
msgstr "Django Beheerder"
#: templates/includes/navbar.html:156
#: templates/includes/navbar.html:158
msgid "Calculator"
msgstr "Rekenmachine"
@@ -2379,69 +2422,88 @@ msgstr "Annuleer"
msgid "Confirm"
msgstr "Bevestig"
#: templates/insights/fragments/category_explorer/index.html:13
#: templates/insights/fragments/category_explorer/charts/account.html:100
#: templates/insights/fragments/category_explorer/charts/currency.html:92
#: templates/monthly_overview/fragments/monthly_account_summary.html:14
#: templates/monthly_overview/fragments/monthly_currency_summary.html:13
#: templates/transactions/fragments/all_account_summary.html:14
#: templates/transactions/fragments/all_currency_summary.html:13
#: templates/transactions/fragments/summary.html:27
#: templates/transactions/fragments/summary.html:42
#: templates/yearly_overview/fragments/account_data.html:12
#: templates/yearly_overview/fragments/currency_data.html:12
msgid "No information to display"
msgstr "Geen informatie om weer te geven"
#: templates/insights/fragments/category_explorer/index.html:14
msgid "Income/Expense by Account"
msgstr ""
msgstr "Inkomsten/uitgaven per rekening"
#: templates/insights/fragments/category_explorer/index.html:25
#, fuzzy
#| msgid "Exchange Currency"
#: templates/insights/fragments/category_explorer/index.html:26
msgid "Income/Expense by Currency"
msgstr "Eenheid Wisselgeld"
msgstr "Inkomsten/uitgaven per Munteenheid"
#: templates/insights/fragments/sankey.html:89
#: templates/insights/fragments/late_transactions.html:15
msgid "All good!"
msgstr "Allemaal goed!"
#: templates/insights/fragments/late_transactions.html:16
msgid "No late transactions"
msgstr "Geen betalingsachterstanden"
#: templates/insights/fragments/latest_transactions.html:14
msgid "No recent transactions"
msgstr "Geen recente betalingen"
#: templates/insights/fragments/sankey.html:93
msgid "From"
msgstr ""
msgstr "Van"
#: templates/insights/fragments/sankey.html:92
#: templates/insights/fragments/sankey.html:96
msgid "Percentage"
msgstr ""
msgstr "Percentage"
#: templates/insights/pages/index.html:33
#, fuzzy
#| msgid "Monthly"
#: templates/insights/pages/index.html:35
msgid "Month"
msgstr "Maandelijks"
msgstr "Maand"
#: templates/insights/pages/index.html:36
#: templates/insights/pages/index.html:38
#: templates/yearly_overview/pages/overview_by_account.html:61
#: templates/yearly_overview/pages/overview_by_currency.html:63
msgid "Year"
msgstr "Jaar"
#: templates/insights/pages/index.html:39
#, fuzzy
#| msgid "Unchanged"
#: templates/insights/pages/index.html:43
msgid "Month Range"
msgstr "Ongewijzigd"
msgstr "Maand Bereik"
#: templates/insights/pages/index.html:42
#: templates/insights/pages/index.html:48
msgid "Year Range"
msgstr ""
msgstr "Jaar Bereik"
#: templates/insights/pages/index.html:45
#, fuzzy
#| msgid "Date and Time"
#: templates/insights/pages/index.html:53
msgid "Date Range"
msgstr "Datum en Tijd"
#: templates/insights/pages/index.html:74
#, fuzzy
#| msgid "Account"
msgid "Account Flow"
msgstr "Rekening"
msgstr "Datum Bereik"
#: templates/insights/pages/index.html:81
#, fuzzy
#| msgid "Currency Code"
msgid "Currency Flow"
msgstr "Munteenheids Code"
msgid "Account Flow"
msgstr "Rekeningstroom"
#: templates/insights/pages/index.html:88
#, fuzzy
#| msgid "Category name"
msgid "Currency Flow"
msgstr "Geldstroom"
#: templates/insights/pages/index.html:95
msgid "Category Explorer"
msgstr "Naam van categorie"
msgstr "Categorie Verkenner"
#: templates/insights/pages/index.html:102
msgid "Late Transactions"
msgstr "Betalingsachterstanden"
#: templates/insights/pages/index.html:108
msgid "Latest Transactions"
msgstr "Laatste Verrichtingen"
#: templates/installment_plans/fragments/add.html:5
msgid "Add installment plan"
@@ -2512,17 +2574,6 @@ msgstr "Artikel"
msgid "No transactions this month"
msgstr "Geen verrichtingen deze maand"
#: templates/monthly_overview/fragments/monthly_account_summary.html:14
#: templates/monthly_overview/fragments/monthly_currency_summary.html:13
#: templates/transactions/fragments/all_account_summary.html:14
#: templates/transactions/fragments/all_currency_summary.html:13
#: templates/transactions/fragments/summary.html:27
#: templates/transactions/fragments/summary.html:42
#: templates/yearly_overview/fragments/account_data.html:12
#: templates/yearly_overview/fragments/currency_data.html:12
msgid "No information to display"
msgstr "Geen informatie om weer te geven"
#: templates/monthly_overview/fragments/monthly_summary.html:6
msgid "Daily Spending Allowance"
msgstr "Dagelijks Toegestane Besteding"
@@ -2543,6 +2594,10 @@ msgstr "actueel"
msgid "projected"
msgstr "verwacht"
#: templates/monthly_overview/fragments/monthly_summary.html:103
msgid "Expenses"
msgstr "Uitgaven"
#: templates/monthly_overview/fragments/monthly_summary.html:167
msgid "Total"
msgstr "Totaal"
@@ -2812,6 +2867,31 @@ msgstr "Bedragen tonen"
msgid "Yearly Overview"
msgstr "Jaaroverzicht"
#, fuzzy
#~| msgid "From Amount"
#~ msgid "Principal Amount"
#~ msgstr "Van Bedrag"
#, fuzzy
#~| msgid "Interval"
#~ msgid "Interest"
#~ msgstr "Interval"
#, fuzzy
#~| msgid "Management"
#~ msgid "Loan Payment"
#~ msgstr "Beheer"
#, fuzzy
#~| msgid "Management"
#~ msgid "Loan Payments"
#~ msgstr "Beheer"
#, fuzzy
#~| msgid "Installment Plans"
#~ msgid "Installment Planss"
#~ msgstr "Afbetalingsplannen"
#, fuzzy
#~| msgid "Start Date"
#~ msgid "Search Date"
@@ -2867,11 +2947,6 @@ msgstr "Jaaroverzicht"
#~ msgid "Reference Date Operator"
#~ msgstr "Referentiedatum vanaf"
#, fuzzy
#~| msgid "From Amount"
#~ msgid "Search Amount"
#~ msgstr "Van Bedrag"
#, fuzzy
#~| msgid "Amount max"
#~ msgid "Amount Operator"

View File

@@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-02-16 13:04-0300\n"
"PO-Revision-Date: 2025-02-16 13:05-0300\n"
"POT-Creation-Date: 2025-02-24 16:30-0300\n"
"PO-Revision-Date: 2025-02-19 23:06-0300\n"
"Last-Translator: Herculino Trotta\n"
"Language-Team: \n"
"Language: pt_BR\n"
@@ -27,10 +27,10 @@ msgstr "Nome do grupo"
#: apps/currencies/forms.py:53 apps/currencies/forms.py:91
#: apps/currencies/forms.py:142 apps/dca/forms.py:49 apps/dca/forms.py:224
#: apps/import_app/forms.py:34 apps/rules/forms.py:45 apps/rules/forms.py:87
#: apps/rules/forms.py:359 apps/transactions/forms.py:190
#: apps/transactions/forms.py:257 apps/transactions/forms.py:581
#: apps/transactions/forms.py:624 apps/transactions/forms.py:656
#: apps/transactions/forms.py:691 apps/transactions/forms.py:827
#: apps/rules/forms.py:359 apps/transactions/forms.py:191
#: apps/transactions/forms.py:258 apps/transactions/forms.py:582
#: apps/transactions/forms.py:625 apps/transactions/forms.py:657
#: apps/transactions/forms.py:692 apps/transactions/forms.py:828
msgid "Update"
msgstr "Atualizar"
@@ -39,10 +39,10 @@ msgstr "Atualizar"
#: apps/currencies/forms.py:99 apps/currencies/forms.py:150
#: apps/dca/forms.py:57 apps/dca/forms.py:232 apps/import_app/forms.py:42
#: apps/rules/forms.py:53 apps/rules/forms.py:95 apps/rules/forms.py:367
#: apps/transactions/forms.py:174 apps/transactions/forms.py:199
#: apps/transactions/forms.py:589 apps/transactions/forms.py:632
#: apps/transactions/forms.py:664 apps/transactions/forms.py:699
#: apps/transactions/forms.py:835
#: apps/transactions/forms.py:176 apps/transactions/forms.py:200
#: apps/transactions/forms.py:590 apps/transactions/forms.py:633
#: apps/transactions/forms.py:665 apps/transactions/forms.py:700
#: apps/transactions/forms.py:836
#: templates/account_groups/fragments/list.html:9
#: templates/accounts/fragments/list.html:9
#: templates/categories/fragments/list.html:9
@@ -70,21 +70,22 @@ msgid "New balance"
msgstr "Novo saldo"
#: apps/accounts/forms.py:119 apps/dca/forms.py:85 apps/dca/forms.py:92
#: apps/insights/forms.py:117 apps/rules/forms.py:168 apps/rules/forms.py:183
#: apps/insights/forms.py:118 apps/rules/forms.py:168 apps/rules/forms.py:183
#: apps/rules/models.py:32 apps/rules/models.py:280
#: apps/transactions/forms.py:39 apps/transactions/forms.py:291
#: apps/transactions/forms.py:298 apps/transactions/forms.py:478
#: apps/transactions/forms.py:723 apps/transactions/models.py:203
#: apps/transactions/forms.py:39 apps/transactions/forms.py:292
#: apps/transactions/forms.py:299 apps/transactions/forms.py:479
#: apps/transactions/forms.py:724 apps/transactions/models.py:203
#: apps/transactions/models.py:378 apps/transactions/models.py:558
msgid "Category"
msgstr "Categoria"
#: apps/accounts/forms.py:126 apps/dca/forms.py:101 apps/dca/forms.py:109
#: apps/export_app/forms.py:38 apps/export_app/forms.py:127
#: apps/rules/forms.py:171 apps/rules/forms.py:180 apps/rules/models.py:33
#: apps/rules/models.py:284 apps/transactions/filters.py:74
#: apps/transactions/forms.py:47 apps/transactions/forms.py:307
#: apps/transactions/forms.py:315 apps/transactions/forms.py:471
#: apps/transactions/forms.py:716 apps/transactions/models.py:209
#: apps/transactions/forms.py:47 apps/transactions/forms.py:308
#: apps/transactions/forms.py:316 apps/transactions/forms.py:472
#: apps/transactions/forms.py:717 apps/transactions/models.py:209
#: apps/transactions/models.py:380 apps/transactions/models.py:562
#: templates/includes/navbar.html:108 templates/tags/fragments/list.html:5
#: templates/tags/pages/index.html:4
@@ -158,13 +159,14 @@ msgstr ""
#: apps/accounts/models.py:59 apps/rules/forms.py:160 apps/rules/forms.py:173
#: apps/rules/models.py:24 apps/rules/models.py:236
#: apps/transactions/forms.py:59 apps/transactions/forms.py:463
#: apps/transactions/forms.py:708 apps/transactions/models.py:176
#: apps/transactions/forms.py:59 apps/transactions/forms.py:464
#: apps/transactions/forms.py:709 apps/transactions/models.py:176
#: apps/transactions/models.py:338 apps/transactions/models.py:540
msgid "Account"
msgstr "Conta"
#: apps/accounts/models.py:60 apps/transactions/filters.py:53
#: apps/accounts/models.py:60 apps/export_app/forms.py:14
#: apps/export_app/forms.py:124 apps/transactions/filters.py:53
#: templates/accounts/fragments/list.html:5
#: templates/accounts/pages/index.html:4 templates/includes/navbar.html:114
#: templates/includes/navbar.html:116
@@ -335,12 +337,12 @@ msgstr "Informação"
msgid "Cache cleared successfully"
msgstr "Cache limpo com sucesso"
#: apps/common/widgets/datepicker.py:47 apps/common/widgets/datepicker.py:186
#: apps/common/widgets/datepicker.py:244
#: apps/common/widgets/datepicker.py:53 apps/common/widgets/datepicker.py:206
#: apps/common/widgets/datepicker.py:264
msgid "Today"
msgstr "Hoje"
#: apps/common/widgets/datepicker.py:123
#: apps/common/widgets/datepicker.py:139
msgid "Now"
msgstr "Agora"
@@ -369,7 +371,7 @@ msgstr "Sufixo"
#: apps/currencies/forms.py:69 apps/dca/models.py:156 apps/rules/forms.py:163
#: apps/rules/forms.py:176 apps/rules/models.py:27 apps/rules/models.py:248
#: apps/transactions/forms.py:63 apps/transactions/forms.py:319
#: apps/transactions/forms.py:63 apps/transactions/forms.py:320
#: apps/transactions/models.py:186
#: templates/dca/fragments/strategy/details.html:52
#: templates/exchange_rates/fragments/table.html:10
@@ -389,7 +391,8 @@ msgstr "Nome da Moeda"
msgid "Decimal Places"
msgstr "Casas Decimais"
#: apps/currencies/models.py:40 apps/transactions/filters.py:60
#: apps/currencies/models.py:40 apps/export_app/forms.py:20
#: apps/export_app/forms.py:125 apps/transactions/filters.py:60
#: templates/currencies/fragments/list.html:5
#: templates/currencies/pages/index.html:4 templates/includes/navbar.html:122
#: templates/includes/navbar.html:124
@@ -419,7 +422,8 @@ msgstr "Taxa de Câmbio"
msgid "Date and Time"
msgstr "Data e Tempo"
#: apps/currencies/models.py:74 templates/exchange_rates/fragments/list.html:6
#: apps/currencies/models.py:74 apps/export_app/forms.py:62
#: apps/export_app/forms.py:137 templates/exchange_rates/fragments/list.html:6
#: templates/exchange_rates/pages/index.html:4
#: templates/includes/navbar.html:126
msgid "Exchange Rates"
@@ -580,11 +584,11 @@ msgstr "Serviços marcados para execução com sucesso"
msgid "Create transaction"
msgstr "Criar transação"
#: apps/dca/forms.py:70 apps/transactions/forms.py:266
#: apps/dca/forms.py:70 apps/transactions/forms.py:267
msgid "From Account"
msgstr "Conta de origem"
#: apps/dca/forms.py:76 apps/transactions/forms.py:271
#: apps/dca/forms.py:76 apps/transactions/forms.py:272
msgid "To Account"
msgstr "Conta de destino"
@@ -609,7 +613,7 @@ msgstr "Conectar transação"
msgid "You must provide an account."
msgstr "Você deve informar uma conta."
#: apps/dca/forms.py:294 apps/transactions/forms.py:413
#: apps/dca/forms.py:294 apps/transactions/forms.py:414
msgid "From and To accounts must be different."
msgstr "As contas De e Para devem ser diferentes."
@@ -628,7 +632,7 @@ msgstr "Moeda de pagamento"
#: apps/dca/models.py:27 apps/dca/models.py:179 apps/rules/forms.py:167
#: apps/rules/forms.py:182 apps/rules/models.py:31 apps/rules/models.py:264
#: apps/transactions/forms.py:333 apps/transactions/models.py:199
#: apps/transactions/forms.py:334 apps/transactions/models.py:199
#: apps/transactions/models.py:387 apps/transactions/models.py:568
msgid "Notes"
msgstr "Notas"
@@ -637,7 +641,7 @@ msgstr "Notas"
msgid "DCA Strategy"
msgstr "Estratégia CMP"
#: apps/dca/models.py:33
#: apps/dca/models.py:33 apps/export_app/forms.py:145
msgid "DCA Strategies"
msgstr "Estratégias CMP"
@@ -657,7 +661,7 @@ msgstr "Quantia recebida"
msgid "DCA Entry"
msgstr "Entrada CMP"
#: apps/dca/models.py:185
#: apps/dca/models.py:185 apps/export_app/forms.py:146
msgid "DCA Entries"
msgstr "Entradas CMP"
@@ -685,6 +689,119 @@ msgstr "Entrada atualizada com sucesso"
msgid "Entry deleted successfully"
msgstr "Entrada apagada com sucesso"
#: apps/export_app/forms.py:26 apps/export_app/forms.py:129
#: apps/transactions/models.py:256 templates/includes/navbar.html:57
#: templates/includes/navbar.html:104
#: templates/recurring_transactions/fragments/list_transactions.html:5
#: templates/recurring_transactions/fragments/table.html:37
#: templates/transactions/pages/transactions.html:5
msgid "Transactions"
msgstr "Transações"
#: apps/export_app/forms.py:32 apps/export_app/forms.py:126
#: apps/transactions/filters.py:67 templates/categories/fragments/list.html:5
#: templates/categories/pages/index.html:4 templates/includes/navbar.html:106
msgid "Categories"
msgstr "Categorias"
#: apps/export_app/forms.py:44 apps/export_app/forms.py:128
#: apps/rules/forms.py:172 apps/rules/forms.py:181 apps/rules/models.py:34
#: apps/rules/models.py:276 apps/transactions/filters.py:81
#: apps/transactions/forms.py:55 apps/transactions/forms.py:487
#: apps/transactions/forms.py:732 apps/transactions/models.py:161
#: apps/transactions/models.py:214 apps/transactions/models.py:383
#: apps/transactions/models.py:565 templates/entities/fragments/list.html:5
#: templates/entities/pages/index.html:4 templates/includes/navbar.html:110
msgid "Entities"
msgstr "Entidades"
#: apps/export_app/forms.py:50 apps/export_app/forms.py:132
#: apps/transactions/models.py:592 templates/includes/navbar.html:74
#: templates/recurring_transactions/fragments/list.html:5
#: templates/recurring_transactions/pages/index.html:4
msgid "Recurring Transactions"
msgstr "Transações Recorrentes"
#: apps/export_app/forms.py:56 apps/export_app/forms.py:130
#: apps/transactions/models.py:391 templates/includes/navbar.html:72
#: templates/installment_plans/fragments/list.html:5
#: templates/installment_plans/pages/index.html:4
msgid "Installment Plans"
msgstr "Parcelamentos"
#: apps/export_app/forms.py:68 apps/export_app/forms.py:135
#: templates/exchange_rates_services/fragments/list.html:6
#: templates/exchange_rates_services/pages/index.html:4
#: templates/includes/navbar.html:138
msgid "Automatic Exchange Rates"
msgstr "Taxas de Câmbio Automáticas"
#: apps/export_app/forms.py:74 templates/includes/navbar.html:132
#: templates/rules/fragments/list.html:5 templates/rules/pages/index.html:4
msgid "Rules"
msgstr "Regras"
#: apps/export_app/forms.py:80 templates/cotton/transaction/item.html:56
msgid "DCA"
msgstr "CMP"
#: apps/export_app/forms.py:86 apps/export_app/forms.py:147
#: templates/import_app/fragments/profiles/list.html:5
#: templates/import_app/pages/profiles_index.html:4
msgid "Import Profiles"
msgstr "Perfis de Importação"
#: apps/export_app/forms.py:112 templates/export_app/fragments/export.html:5
#: templates/export_app/pages/index.html:15
msgid "Export"
msgstr "Exportar"
#: apps/export_app/forms.py:121
msgid "Import a ZIP file exported from WYGIWYH"
msgstr "Importe um arquivo ZIP exportado do WYGIWYH"
#: apps/export_app/forms.py:122
msgid "ZIP File"
msgstr "Arquivo ZIP"
#: apps/export_app/forms.py:138 apps/rules/models.py:16
msgid "Transaction rules"
msgstr "Regra da transação"
#: apps/export_app/forms.py:140 apps/rules/models.py:53
msgid "Edit transaction action"
msgstr "Ação de editar de transação"
#: apps/export_app/forms.py:143 apps/rules/models.py:290
msgid "Update or create transaction actions"
msgstr "Ações de atualizar ou criar transação"
#: apps/export_app/forms.py:176 templates/cotton/transaction/item.html:158
#: templates/cotton/ui/deleted_transactions_action_bar.html:47
#: templates/export_app/fragments/restore.html:5
#: templates/export_app/pages/index.html:24
msgid "Restore"
msgstr "Restaurar"
#: apps/export_app/forms.py:187
msgid "Please upload either a ZIP file or at least one CSV file"
msgstr "Carregue um arquivo ZIP ou pelo menos um arquivo CSV"
#: apps/export_app/views.py:168
msgid "You have to select at least one export"
msgstr "É necessário selecionar pelo menos uma exportação"
#: apps/export_app/views.py:186
msgid "Data restored successfully"
msgstr "Dados restaurados com sucesso"
#: apps/export_app/views.py:198
msgid ""
"There was an error restoring your data. Check the logs for more details."
msgstr ""
"Ocorreu um erro ao restaurar seus dados. Verifique o log para obter mais "
"detalhes."
#: apps/import_app/forms.py:49
msgid "Select a file"
msgstr "Selecione um arquivo"
@@ -759,31 +876,47 @@ msgstr "Importação adicionada à fila com sucesso"
msgid "Run deleted successfully"
msgstr "Importação apagada com sucesso"
#: apps/insights/utils/category_explorer.py:44
#: apps/insights/utils/category_explorer.py:93 apps/transactions/models.py:170
#: templates/calendar_view/fragments/list.html:42
#: templates/calendar_view/fragments/list.html:44
#: templates/calendar_view/fragments/list.html:52
#: templates/calendar_view/fragments/list.html:54
#: templates/cotton/ui/quick_transactions_buttons.html:10
#: templates/insights/fragments/category_explorer/charts/account.html:54
#: templates/insights/fragments/category_explorer/charts/currency.html:55
#: templates/monthly_overview/fragments/monthly_summary.html:39
msgid "Income"
msgstr "Renda"
#: apps/insights/utils/category_explorer.py:48
#: apps/insights/utils/category_explorer.py:97
#: templates/insights/fragments/category_explorer/charts/account.html:60
#: templates/insights/fragments/category_explorer/charts/currency.html:61
#: templates/monthly_overview/fragments/monthly_summary.html:103
msgid "Expenses"
msgstr "Despesas"
#: apps/insights/utils/sankey.py:36 apps/insights/utils/sankey.py:167
#: apps/insights/forms.py:119 apps/insights/utils/sankey.py:36
#: apps/insights/utils/sankey.py:167
msgid "Uncategorized"
msgstr "Sem categoria"
#: apps/insights/utils/category_explorer.py:66
#: apps/insights/utils/category_explorer.py:145
#: templates/cotton/ui/percentage_distribution.html:10
#: templates/cotton/ui/percentage_distribution.html:14
#: templates/insights/fragments/category_explorer/charts/account.html:72
#: templates/insights/fragments/category_explorer/charts/currency.html:72
msgid "Current Income"
msgstr "Renda Atual"
#: apps/insights/utils/category_explorer.py:70
#: apps/insights/utils/category_explorer.py:149
#: templates/cotton/ui/percentage_distribution.html:24
#: templates/cotton/ui/percentage_distribution.html:28
#: templates/insights/fragments/category_explorer/charts/account.html:66
#: templates/insights/fragments/category_explorer/charts/currency.html:66
msgid "Current Expenses"
msgstr "Despesas Atuais"
#: apps/insights/utils/category_explorer.py:74
#: apps/insights/utils/category_explorer.py:153
#: templates/cotton/ui/percentage_distribution.html:3
#: templates/cotton/ui/percentage_distribution.html:7
#: templates/insights/fragments/category_explorer/charts/account.html:78
#: templates/insights/fragments/category_explorer/charts/currency.html:78
msgid "Projected Income"
msgstr "Renda Prevista"
#: apps/insights/utils/category_explorer.py:78
#: apps/insights/utils/category_explorer.py:157
#: templates/cotton/ui/percentage_distribution.html:17
#: templates/cotton/ui/percentage_distribution.html:21
#: templates/insights/fragments/category_explorer/charts/account.html:60
#: templates/insights/fragments/category_explorer/charts/currency.html:60
msgid "Projected Expenses"
msgstr "Despesas Previstas"
#: apps/insights/utils/sankey.py:133 apps/insights/utils/sankey.py:134
#: apps/insights/utils/sankey.py:263 apps/insights/utils/sankey.py:264
msgid "Saved"
@@ -805,7 +938,7 @@ msgstr "Se..."
msgid "Set field"
msgstr "Definir campo"
#: apps/rules/forms.py:65 templates/insights/fragments/sankey.html:90
#: apps/rules/forms.py:65 templates/insights/fragments/sankey.html:94
msgid "To"
msgstr "Para"
@@ -837,8 +970,8 @@ msgid "Paid"
msgstr "Pago"
#: apps/rules/forms.py:164 apps/rules/forms.py:177 apps/rules/models.py:28
#: apps/rules/models.py:252 apps/transactions/forms.py:66
#: apps/transactions/forms.py:322 apps/transactions/forms.py:492
#: apps/rules/models.py:252 apps/transactions/forms.py:67
#: apps/transactions/forms.py:323 apps/transactions/forms.py:493
#: apps/transactions/models.py:187 apps/transactions/models.py:361
#: apps/transactions/models.py:570
msgid "Reference Date"
@@ -846,13 +979,13 @@ msgstr "Data de Referência"
#: apps/rules/forms.py:165 apps/rules/forms.py:178 apps/rules/models.py:29
#: apps/rules/models.py:256 apps/transactions/models.py:192
#: apps/transactions/models.py:551 templates/insights/fragments/sankey.html:91
#: apps/transactions/models.py:551 templates/insights/fragments/sankey.html:95
msgid "Amount"
msgstr "Quantia"
#: apps/rules/forms.py:166 apps/rules/forms.py:179 apps/rules/models.py:11
#: apps/rules/models.py:30 apps/rules/models.py:260
#: apps/transactions/forms.py:325 apps/transactions/models.py:197
#: apps/transactions/forms.py:326 apps/transactions/models.py:197
#: apps/transactions/models.py:345 apps/transactions/models.py:554
msgid "Description"
msgstr "Descrição"
@@ -867,16 +1000,6 @@ msgstr "Nota Interna"
msgid "Internal ID"
msgstr "ID Interna"
#: apps/rules/forms.py:172 apps/rules/forms.py:181 apps/rules/models.py:34
#: apps/rules/models.py:276 apps/transactions/filters.py:81
#: apps/transactions/forms.py:55 apps/transactions/forms.py:486
#: apps/transactions/forms.py:731 apps/transactions/models.py:161
#: apps/transactions/models.py:214 apps/transactions/models.py:383
#: apps/transactions/models.py:565 templates/entities/fragments/list.html:5
#: templates/entities/pages/index.html:4 templates/includes/navbar.html:110
msgid "Entities"
msgstr "Entidades"
#: apps/rules/forms.py:199
msgid "Search Criteria"
msgstr "Critério de Busca"
@@ -893,10 +1016,6 @@ msgstr "Gatilho"
msgid "Transaction rule"
msgstr "Regra da transação"
#: apps/rules/models.py:16
msgid "Transaction rules"
msgstr "Regra da transação"
#: apps/rules/models.py:40 apps/rules/models.py:78
msgid "Rule"
msgstr "Regra"
@@ -909,13 +1028,9 @@ msgstr "Campo"
msgid "Value"
msgstr "Valor"
#: apps/rules/models.py:53
msgid "Edit transaction action"
msgstr "Editar ação de transação"
#: apps/rules/models.py:54
msgid "Edit transaction actions"
msgstr "Editar ações de transação"
msgstr "Ações de editar de transação"
#: apps/rules/models.py:64
msgid "is exactly"
@@ -967,11 +1082,7 @@ msgstr ""
#: apps/rules/models.py:289
msgid "Update or create transaction action"
msgstr "Atualizar ou criar transação ação"
#: apps/rules/models.py:290
msgid "Update or create transaction actions"
msgstr "Atualizar ou criar transação ações"
msgstr "Ação de atualizar ou criar transação"
#: apps/rules/views.py:52
msgid "Rule deactivated successfully"
@@ -1028,11 +1139,6 @@ msgstr "Conteúdo"
msgid "Transaction Type"
msgstr "Tipo de Transação"
#: apps/transactions/filters.py:67 templates/categories/fragments/list.html:5
#: templates/categories/pages/index.html:4 templates/includes/navbar.html:106
msgid "Categories"
msgstr "Categorias"
#: apps/transactions/filters.py:91
msgid "Date from"
msgstr "Data de"
@@ -1053,40 +1159,40 @@ msgstr "Quantia miníma"
msgid "Amount max"
msgstr "Quantia máxima"
#: apps/transactions/forms.py:158
#: apps/transactions/forms.py:160
msgid "More"
msgstr "Mais"
#: apps/transactions/forms.py:278
#: apps/transactions/forms.py:279
msgid "From Amount"
msgstr "Quantia de origem"
#: apps/transactions/forms.py:283
#: apps/transactions/forms.py:284
msgid "To Amount"
msgstr "Quantia de destino"
#: apps/transactions/forms.py:398
#: apps/transactions/forms.py:399
#: templates/cotton/ui/quick_transactions_buttons.html:40
msgid "Transfer"
msgstr "Transferir"
#: apps/transactions/forms.py:610
#: apps/transactions/forms.py:611
msgid "Tag name"
msgstr "Nome da Tag"
#: apps/transactions/forms.py:642
#: apps/transactions/forms.py:643
msgid "Entity name"
msgstr "Nome da entidade"
#: apps/transactions/forms.py:674
#: apps/transactions/forms.py:675
msgid "Category name"
msgstr "Nome da Categoria"
#: apps/transactions/forms.py:676
#: apps/transactions/forms.py:677
msgid "Muted categories won't count towards your monthly total"
msgstr "As categorias silenciadas não serão contabilizadas em seu total mensal"
#: apps/transactions/forms.py:846
#: apps/transactions/forms.py:847
msgid "End date should be after the start date"
msgstr "Data final deve ser após data inicial"
@@ -1132,6 +1238,16 @@ msgstr ""
msgid "Entity"
msgstr "Entidade"
#: apps/transactions/models.py:170
#: templates/calendar_view/fragments/list.html:42
#: templates/calendar_view/fragments/list.html:44
#: templates/calendar_view/fragments/list.html:52
#: templates/calendar_view/fragments/list.html:54
#: templates/cotton/ui/quick_transactions_buttons.html:10
#: templates/monthly_overview/fragments/monthly_summary.html:39
msgid "Income"
msgstr "Renda"
#: apps/transactions/models.py:171
#: templates/calendar_view/fragments/list.html:46
#: templates/calendar_view/fragments/list.html:48
@@ -1161,14 +1277,6 @@ msgstr "Apagado Em"
msgid "Transaction"
msgstr "Transação"
#: apps/transactions/models.py:256 templates/includes/navbar.html:57
#: templates/includes/navbar.html:104
#: templates/recurring_transactions/fragments/list_transactions.html:5
#: templates/recurring_transactions/fragments/table.html:37
#: templates/transactions/pages/transactions.html:5
msgid "Transactions"
msgstr "Transações"
#: apps/transactions/models.py:323 templates/tags/fragments/table.html:53
msgid "No tags"
msgstr "Nenhuma tag"
@@ -1226,12 +1334,6 @@ msgstr "Recorrência"
msgid "Installment Amount"
msgstr "Valor da Parcela"
#: apps/transactions/models.py:391 templates/includes/navbar.html:72
#: templates/installment_plans/fragments/list.html:5
#: templates/installment_plans/pages/index.html:4
msgid "Installment Plans"
msgstr "Parcelamentos"
#: apps/transactions/models.py:533
msgid "day(s)"
msgstr "dia(s)"
@@ -1269,12 +1371,6 @@ msgstr "Última data gerada"
msgid "Last Generated Reference Date"
msgstr "Última data de referência gerada"
#: apps/transactions/models.py:592 templates/includes/navbar.html:74
#: templates/recurring_transactions/fragments/list.html:5
#: templates/recurring_transactions/pages/index.html:4
msgid "Recurring Transactions"
msgstr "Transações Recorrentes"
#: apps/transactions/validators.py:8
#, python-format
msgid "%(value)s has too many decimal places. Maximum is 30."
@@ -1796,6 +1892,7 @@ msgstr "Fechar"
#: templates/cotton/config/search.html:6
#: templates/import_app/fragments/profiles/list_presets.html:13
#: templates/monthly_overview/pages/overview.html:177
msgid "Search"
msgstr "Buscar"
@@ -1803,20 +1900,11 @@ msgstr "Buscar"
msgid "Select"
msgstr "Selecionar"
#: templates/cotton/transaction/item.html:56
msgid "DCA"
msgstr "CMP"
#: templates/cotton/transaction/item.html:137
#: templates/cotton/ui/transactions_action_bar.html:78
msgid "Duplicate"
msgstr "Duplicar"
#: templates/cotton/transaction/item.html:158
#: templates/cotton/ui/deleted_transactions_action_bar.html:47
msgid "Restore"
msgstr "Restaurar"
#: templates/cotton/ui/account_card.html:15
#: templates/cotton/ui/currency_card.html:10
msgid "projected income"
@@ -1920,26 +2008,6 @@ msgstr "Minímo"
msgid "Count"
msgstr "Contagem"
#: templates/cotton/ui/percentage_distribution.html:3
#: templates/cotton/ui/percentage_distribution.html:7
msgid "Projected Income"
msgstr "Renda Prevista"
#: templates/cotton/ui/percentage_distribution.html:10
#: templates/cotton/ui/percentage_distribution.html:14
msgid "Current Income"
msgstr "Renda Atual"
#: templates/cotton/ui/percentage_distribution.html:17
#: templates/cotton/ui/percentage_distribution.html:21
msgid "Projected Expenses"
msgstr "Despesas Previstas"
#: templates/cotton/ui/percentage_distribution.html:24
#: templates/cotton/ui/percentage_distribution.html:28
msgid "Current Expenses"
msgstr "Despesas Atuais"
#: templates/cotton/ui/quick_transactions_buttons.html:25
msgid "Installment"
msgstr "Parcelamento"
@@ -2141,12 +2209,6 @@ msgstr "Nenhuma taxa de câmbio"
msgid "Page navigation"
msgstr "Navegação por página"
#: templates/exchange_rates_services/fragments/list.html:6
#: templates/exchange_rates_services/pages/index.html:4
#: templates/includes/navbar.html:136
msgid "Automatic Exchange Rates"
msgstr "Taxas de Câmbio Automáticas"
#: templates/exchange_rates_services/fragments/list.html:21
msgid "Fetch all"
msgstr "Executar todos"
@@ -2175,6 +2237,10 @@ msgstr "contas"
msgid "No services configured"
msgstr "Nenhum serviço configurado"
#: templates/export_app/pages/index.html:4 templates/includes/navbar.html:136
msgid "Export and Restore"
msgstr "Exportar e Restaurar"
#: templates/import_app/fragments/profiles/add.html:6
msgid "Add new import profile"
msgstr "Adicionar novo perfil de importação"
@@ -2187,11 +2253,6 @@ msgstr "Uma mensagem do autor"
msgid "Edit import profile"
msgstr "Editar perfil de importação"
#: templates/import_app/fragments/profiles/list.html:5
#: templates/import_app/pages/profiles_index.html:4
msgid "Import Profiles"
msgstr "Perfis de Importação"
#: templates/import_app/fragments/profiles/list.html:17
msgid "New"
msgstr "Novo"
@@ -2318,20 +2379,15 @@ msgstr "Gerenciar"
msgid "Automation"
msgstr "Automação"
#: templates/includes/navbar.html:132 templates/rules/fragments/list.html:5
#: templates/rules/pages/index.html:4
msgid "Rules"
msgstr "Regras"
#: templates/includes/navbar.html:146
#: templates/includes/navbar.html:148
msgid "Only use this if you know what you're doing"
msgstr "Só use isso se você souber o que está fazendo"
#: templates/includes/navbar.html:147
#: templates/includes/navbar.html:149
msgid "Django Admin"
msgstr "Django Admin"
#: templates/includes/navbar.html:156
#: templates/includes/navbar.html:158
msgid "Calculator"
msgstr "Calculadora"
@@ -2364,56 +2420,89 @@ msgstr "Cancelar"
msgid "Confirm"
msgstr "Confirmar"
#: templates/insights/fragments/category_explorer/index.html:13
#: templates/insights/fragments/category_explorer/charts/account.html:100
#: templates/insights/fragments/category_explorer/charts/currency.html:92
#: templates/monthly_overview/fragments/monthly_account_summary.html:14
#: templates/monthly_overview/fragments/monthly_currency_summary.html:13
#: templates/transactions/fragments/all_account_summary.html:14
#: templates/transactions/fragments/all_currency_summary.html:13
#: templates/transactions/fragments/summary.html:27
#: templates/transactions/fragments/summary.html:42
#: templates/yearly_overview/fragments/account_data.html:12
#: templates/yearly_overview/fragments/currency_data.html:12
msgid "No information to display"
msgstr "Não há informação para mostrar"
#: templates/insights/fragments/category_explorer/index.html:14
msgid "Income/Expense by Account"
msgstr "Gasto/Despesa por Conta"
#: templates/insights/fragments/category_explorer/index.html:25
#: templates/insights/fragments/category_explorer/index.html:26
msgid "Income/Expense by Currency"
msgstr "Gasto/Despesa por Moeda"
#: templates/insights/fragments/sankey.html:89
#: templates/insights/fragments/late_transactions.html:15
msgid "All good!"
msgstr "Tudo certo!"
#: templates/insights/fragments/late_transactions.html:16
msgid "No late transactions"
msgstr "Nenhuma transação atrasada"
#: templates/insights/fragments/latest_transactions.html:14
msgid "No recent transactions"
msgstr "Nenhuma transação recente"
#: templates/insights/fragments/sankey.html:93
msgid "From"
msgstr "De"
#: templates/insights/fragments/sankey.html:92
#: templates/insights/fragments/sankey.html:96
msgid "Percentage"
msgstr "Porcentagem"
#: templates/insights/pages/index.html:33
#: templates/insights/pages/index.html:35
msgid "Month"
msgstr "Mês"
#: templates/insights/pages/index.html:36
#: templates/insights/pages/index.html:38
#: templates/yearly_overview/pages/overview_by_account.html:61
#: templates/yearly_overview/pages/overview_by_currency.html:63
msgid "Year"
msgstr "Ano"
#: templates/insights/pages/index.html:39
#: templates/insights/pages/index.html:43
msgid "Month Range"
msgstr "Intervalo de Mês"
#: templates/insights/pages/index.html:42
#: templates/insights/pages/index.html:48
msgid "Year Range"
msgstr "Intervalo de Ano"
#: templates/insights/pages/index.html:45
#: templates/insights/pages/index.html:53
msgid "Date Range"
msgstr "Intervalo de Data"
#: templates/insights/pages/index.html:74
#: templates/insights/pages/index.html:81
msgid "Account Flow"
msgstr "Fluxo de Conta"
#: templates/insights/pages/index.html:81
#: templates/insights/pages/index.html:88
msgid "Currency Flow"
msgstr "Fluxo de Moeda"
#: templates/insights/pages/index.html:88
#: templates/insights/pages/index.html:95
msgid "Category Explorer"
msgstr "Explorador de Categoria"
#: templates/insights/pages/index.html:102
msgid "Late Transactions"
msgstr "Transações Atrasadas"
#: templates/insights/pages/index.html:108
msgid "Latest Transactions"
msgstr "Últimas Transações"
#: templates/installment_plans/fragments/add.html:5
msgid "Add installment plan"
msgstr "Adicionar parcelamento"
@@ -2483,17 +2572,6 @@ msgstr "Item"
msgid "No transactions this month"
msgstr "Nenhuma transação neste mês"
#: templates/monthly_overview/fragments/monthly_account_summary.html:14
#: templates/monthly_overview/fragments/monthly_currency_summary.html:13
#: templates/transactions/fragments/all_account_summary.html:14
#: templates/transactions/fragments/all_currency_summary.html:13
#: templates/transactions/fragments/summary.html:27
#: templates/transactions/fragments/summary.html:42
#: templates/yearly_overview/fragments/account_data.html:12
#: templates/yearly_overview/fragments/currency_data.html:12
msgid "No information to display"
msgstr "Não há informação para mostrar"
#: templates/monthly_overview/fragments/monthly_summary.html:6
msgid "Daily Spending Allowance"
msgstr "Gasto Diário"
@@ -2514,6 +2592,10 @@ msgstr "atual"
msgid "projected"
msgstr "previsto"
#: templates/monthly_overview/fragments/monthly_summary.html:103
msgid "Expenses"
msgstr "Despesas"
#: templates/monthly_overview/fragments/monthly_summary.html:167
msgid "Total"
msgstr "Total"
@@ -2780,6 +2862,31 @@ msgstr "Mostrar valores"
msgid "Yearly Overview"
msgstr "Visão Anual"
#, fuzzy
#~| msgid "From Amount"
#~ msgid "Principal Amount"
#~ msgstr "Quantia de origem"
#, fuzzy
#~| msgid "Interval"
#~ msgid "Interest"
#~ msgstr "Intervalo"
#, fuzzy
#~| msgid "Management"
#~ msgid "Loan Payment"
#~ msgstr "Gerenciar"
#, fuzzy
#~| msgid "Management"
#~ msgid "Loan Payments"
#~ msgstr "Gerenciar"
#, fuzzy
#~| msgid "Installment Plans"
#~ msgid "Installment Planss"
#~ msgstr "Parcelamentos"
#, fuzzy
#~| msgid "Tags"
#~ msgid "No Tags"
@@ -2840,11 +2947,6 @@ msgstr "Visão Anual"
#~ msgid "Reference Date Operator"
#~ msgstr "Data de Referência de"
#, fuzzy
#~| msgid "From Amount"
#~ msgid "Search Amount"
#~ msgstr "Quantia de origem"
#, fuzzy
#~| msgid "Amount max"
#~ msgid "Amount Operator"

View File

@@ -1,7 +1,7 @@
<div class="row {% if not remove_padding %}p-5{% endif %}">
<div class="col {% if not remove_padding %}p-5{% endif %}">
<div class="text-center">
<i class="fa-solid fa-circle-xmark tw-text-6xl"></i>
<i class="{% if icon %}{{ icon }}{% else %}fa-solid fa-circle-xmark{% endif %} tw-text-6xl"></i>
<p class="lead mt-4 mb-0">{{ title }}</p>
<p class="tw-text-gray-500">{{ subtitle }}</p>
</div>

View File

@@ -1,7 +1,7 @@
{% load markdown %}
{% load i18n %}
<div class="transaction">
<div class="d-flex my-1 {% if transaction.type == "EX" %}expense{% else %}income{% endif %}">
<div class="transaction {% if transaction.type == "EX" %}expense{% else %}income{% endif %}">
<div class="d-flex my-1">
{% if not disable_selection %}
<label class="px-3 d-flex align-items-center justify-content-center">
<input class="form-check-input" type="checkbox" name="transactions" value="{{ transaction.id }}"
@@ -15,9 +15,9 @@
_="on mouseover remove .tw-invisible from the first .transaction-actions in me end
on mouseout add .tw-invisible to the first .transaction-actions in me end">
<div class="row font-monospace tw-text-sm align-items-center">
<div class="col-lg-1 col-12 d-flex align-items-center tw-text-2xl lg:tw-text-xl text-lg-center text-center">
<div class="col-lg-auto col-12 d-flex align-items-center tw-text-2xl lg:tw-text-xl text-lg-center text-center p-0 ps-1">
{% if not transaction.deleted %}
<a class="text-decoration-none my-lg-3 mx-lg-3 mx-2 my-2 tw-text-gray-500"
<a class="text-decoration-none p-3 tw-text-gray-500"
title="{% if transaction.is_paid %}{% trans 'Paid' %}{% else %}{% trans 'Projected' %}{% endif %}"
role="button"
hx-get="{% url 'transaction_pay' transaction_id=transaction.id %}"
@@ -27,14 +27,14 @@
class="fa-regular fa-circle"></i>{% endif %}
</a>
{% else %}
<div class="text-decoration-none my-lg-3 mx-lg-3 mx-2 my-2 tw-text-gray-500"
<div class="text-decoration-none p-3 tw-text-gray-500"
title="{% if transaction.is_paid %}{% trans 'Paid' %}{% else %}{% trans 'Projected' %}{% endif %}">
{% if transaction.is_paid %}<i class="fa-regular fa-circle-check"></i>{% else %}<i
class="fa-regular fa-circle"></i>{% endif %}
</div>
{% endif %}
</div>
<div class="col-lg-8 col-12">
<div class="col-lg col-12">
{# Date#}
<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>
@@ -92,7 +92,7 @@
{% endwith %}
</div>
</div>
<div class="col-lg-3 col-12 text-lg-end align-self-end">
<div class="col-lg-auto col-12 text-lg-end align-self-end">
<div class="main-amount mb-2 mb-lg-0">
<c-amount.display
:amount="transaction.amount"

View File

@@ -1,9 +1,9 @@
<div class="card tw-relative h-100 shadow">
<div class="tw-absolute tw-h-8 tw-w-8 tw-right-2 tw-top-2 tw-bg-{{ color }}-300 tw-text-{{ color }}-800 text-center align-items-center d-flex justify-content-center rounded-2">
<i class="{{ icon }}"></i>
{% if icon %}<i class="{{ icon }}"></i>{% else %}<span class="fw-bold">{{ title.0 }}</span>{% endif %}
</div>
<div class="card-body">
<h5 class="tw-text-{{ color }}-400 fw-bold tw-mr-[50px]" {{ attrs }}>{{ title }}{% if help_text %}{% include 'includes/help_icon.html' with content=help_text %}{% endif %}</h5>
{{ slot }}
</div>
</div>
</div>

View File

@@ -27,7 +27,7 @@
<ul class="dropdown-menu">
<li>
<div class="dropdown-item px-3 tw-cursor-pointer"
_="on click set <#transactions-list input[type='checkbox']/>'s checked to true then call me.blur() then trigger change">
_="on click set <#transactions-list .transaction:not([style*='display: none']) input[type='checkbox']/>'s checked to true then call me.blur() then trigger change">
<i class="fa-regular fa-square-check tw-text-green-400 me-3"></i>{% translate 'Select All' %}
</div>
</li>

View File

@@ -0,0 +1,13 @@
{% extends "extends/offcanvas.html" %}
{% load crispy_forms_tags %}
{% load i18n %}
{% block title %}{% translate 'Export' %}{% endblock %}
{% block body %}
<div class="container p-3">
<form method="post" action="{% url 'export_form' %}" id="export-form" class="show-loading px-1" _="on submit trigger hide_offcanvas" target="_blank">
{% crispy form %}
</form>
</div>
{% endblock %}

View File

@@ -0,0 +1,17 @@
{% extends "extends/offcanvas.html" %}
{% load crispy_forms_tags %}
{% load i18n %}
{% block title %}{% translate 'Restore' %}{% endblock %}
{% block body %}
<div class="container p-3">
<form hx-post="{% url 'restore_form' %}"
hx-target="#generic-offcanvas"
id="restore-form"
enctype="multipart/form-data"
class="show-loading px-1">
{% crispy form %}
</form>
</div>
{% endblock %}

View File

@@ -0,0 +1,29 @@
{% extends "layouts/base.html" %}
{% load i18n %}
{% block title %}{% translate 'Export and Restore' %}{% endblock %}
{% block content %}
<div class="container">
<div class="row d-flex flex-row align-items-center justify-content-center my-5">
<div class="text-center w-auto mb-3">
<button class="btn btn-outline-success d-flex flex-column align-items-center justify-content-center p-3"
style="width: 100px; height: 100px;"
hx-get="{% url 'export_form' %}"
hx-target="#generic-offcanvas">
<i class="fa-solid fa-download mb-1"></i>
<span>{% trans 'Export' %}</span>
</button>
</div>
<div class="text-center w-auto mb-3">
<button class="btn btn-outline-primary d-flex flex-column align-items-center justify-content-center p-3"
style="width: 100px; height: 100px;"
hx-get="{% url 'restore_form' %}"
hx-target="#generic-offcanvas">
<i class="fa-solid fa-upload mb-1"></i>
<span>{% trans 'Restore' %}</span>
</button>
</div>
</div>
</div>
{% endblock %}

View File

@@ -94,7 +94,7 @@
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle {% active_link views='tags_index||entities_index||categories_index||accounts_index||account_groups_index||currencies_index||exchange_rates_index||rules_index||import_profiles_index||automatic_exchange_rates_index' %}"
<a class="nav-link dropdown-toggle {% active_link views='tags_index||entities_index||categories_index||accounts_index||account_groups_index||currencies_index||exchange_rates_index||rules_index||import_profiles_index||automatic_exchange_rates_index||export_index' %}"
href="#" role="button"
data-bs-toggle="dropdown"
aria-expanded="false">
@@ -132,6 +132,8 @@
href="{% url 'rules_index' %}">{% translate 'Rules' %}</a></li>
<li><a class="dropdown-item {% active_link views='import_profiles_index' %}"
href="{% url 'import_profiles_index' %}">{% translate 'Import' %} <span class="badge text-bg-primary">beta</span></a></li>
<li><a class="dropdown-item {% active_link views='export_index' %}"
href="{% url 'export_index' %}">{% translate 'Export and Restore' %}</a></li>
<li><a class="dropdown-item {% active_link views='automatic_exchange_rates_index' %}"
href="{% url 'automatic_exchange_rates_index' %}">{% translate 'Automatic Exchange Rates' %}</a></li>
<li>

View File

@@ -1,79 +1,101 @@
{% load i18n %}
<div class="chart-container" style="position: relative; height:400px; width:100%" _="init call setupAccountChart() end">
<canvas id="accountChart"></canvas>
</div>
{% if account_data.labels %}
<div class="chart-container" style="position: relative; height:400px; width:100%"
_="init call setupAccountChart() end">
<canvas id="accountChart"></canvas>
</div>
<script>
// Get the data from your Django view (passed as JSON)
var accountData = {{ account_data|safe }};
<script>
// Get the data from your Django view (passed as JSON)
var accountData = {{ account_data|safe }};
function setupAccountChart() {
var chartOptions = {
indexAxis: 'y', // This makes the chart horizontal
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
stacked: true, // Enable stacking on the x-axis
title: {
display: false,
},
},
y: {
stacked: true, // Enable stacking on the y-axis
title: {
display: false,
},
}
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
label: function (context) {
if (context.parsed.x !== null) {
return new Intl.NumberFormat(undefined).format(Math.abs(context.parsed.x)); // Using abs for display
}
return "";
},
}
}
}
};
function setupAccountChart() {
var chartOptions = {
indexAxis: 'y', // This makes the chart horizontal
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
stacked: true, // Enable stacking on the x-axis
title: {
display: false,
},
},
y: {
stacked: true, // Enable stacking on the y-axis
title: {
display: false,
},
}
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
label: function (context) {
if (context.parsed.x !== null) {
return `${context.dataset.label}: ${new Intl.NumberFormat(undefined, {
minimumFractionDigits: 0,
maximumFractionDigits: 30,
roundingMode: 'trunc'
}).format(Math.abs(context.parsed.x))}`;
}
return "";
},
}
}
}
};
new Chart(
document.getElementById('accountChart'),
{
type: 'bar',
data: {
labels: accountData.labels,
datasets: [
{
label: "{% trans 'Income' %}",
data: accountData.datasets[0].data,
backgroundColor: '#4dde80',
stack: 'stack0'
},
{
label: "{% trans 'Expenses' %}",
data: accountData.datasets[1].data,
backgroundColor: '#f87171',
stack: 'stack0'
}
]
},
options: {
...chartOptions,
plugins: {
...chartOptions.plugins,
title: {
display: false,
}
}
}
}
);
}
</script>
new Chart(
document.getElementById('accountChart'),
{
type: 'bar',
data: {
labels: accountData.labels,
datasets: [
{
label: "{% trans 'Projected Expenses' %}",
data: accountData.datasets[3].data,
backgroundColor: '#f8717180', // Added transparency
stack: 'stack0'
},
{
label: "{% trans 'Current Expenses' %}",
data: accountData.datasets[1].data,
backgroundColor: '#f87171',
stack: 'stack0'
},
{
label: "{% trans 'Current Income' %}",
data: accountData.datasets[0].data,
backgroundColor: '#4dde80',
stack: 'stack0'
},
{
label: "{% trans 'Projected Income' %}",
data: accountData.datasets[2].data,
backgroundColor: '#4dde8080', // Added transparency
stack: 'stack0'
},
]
},
options: {
...chartOptions,
plugins: {
...chartOptions.plugins,
title: {
display: false,
}
}
}
}
);
}
</script>
{% else %}
<c-msg.empty title="{% translate "No information to display" %}"></c-msg.empty>
{% endif %}

View File

@@ -1,80 +1,93 @@
{% load i18n %}
<div class="chart-container" style="position: relative; height:400px; width:100%"
_="init call setupCurrencyChart() end">
<canvas id="currencyChart"></canvas>
</div>
{% if currency_data.labels %}
<div class="chart-container" style="position: relative; height:400px; width:100%"
_="init call setupCurrencyChart() end">
<canvas id="currencyChart"></canvas>
</div>
<script>
// Get the data from your Django view (passed as JSON)
var currencyData = {{ currency_data|safe }};
<script>
// Get the data from your Django view (passed as JSON)
var currencyData = {{ currency_data|safe }};
function setupCurrencyChart() {
var chartOptions = {
indexAxis: 'y', // This makes the chart horizontal
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
stacked: true, // Enable stacking on the x-axis
title: {
display: false,
},
},
y: {
stacked: true, // Enable stacking on the y-axis
title: {
display: false,
},
}
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
label: function (context) {
if (context.parsed.x !== null) {
return new Intl.NumberFormat(undefined).format(Math.abs(context.parsed.x)); // Using abs for display
}
return "";
},
}
}
}
};
function setupCurrencyChart() {
var chartOptions = {
indexAxis: 'y',
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
stacked: true,
title: {
display: false,
},
},
y: {
stacked: true,
title: {
display: false,
},
}
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
label: function (context) {
if (context.parsed.x !== null) {
return `${context.dataset.label}: ${new Intl.NumberFormat(undefined, {
minimumFractionDigits: 0,
maximumFractionDigits: 30,
roundingMode: 'trunc'
}).format(Math.abs(context.parsed.x))}`;
}
return "";
},
}
}
}
};
new Chart(
document.getElementById('currencyChart'),
{
type: 'bar',
data: {
labels: currencyData.labels,
datasets: [
{
label: "{% trans 'Income' %}",
data: currencyData.datasets[0].data,
backgroundColor: '#4dde80',
stack: 'stack0'
},
{
label: "{% trans 'Expenses' %}",
data: currencyData.datasets[1].data,
backgroundColor: '#f87171',
stack: 'stack0'
}
]
},
options: {
...chartOptions,
plugins: {
...chartOptions.plugins,
title: {
display: false,
}
}
}
}
);
}
</script>
new Chart(
document.getElementById('currencyChart'),
{
type: 'bar',
data: {
labels: currencyData.labels,
datasets: [
{
label: "{% trans 'Projected Expenses' %}",
data: currencyData.datasets[3].data,
backgroundColor: '#f8717180', // Added transparency
stack: 'stack0'
},
{
label: "{% trans 'Current Expenses' %}",
data: currencyData.datasets[1].data,
backgroundColor: '#f87171',
stack: 'stack0'
},
{
label: "{% trans 'Current Income' %}",
data: currencyData.datasets[0].data,
backgroundColor: '#4dde80',
stack: 'stack0'
},
{
label: "{% trans 'Projected Income' %}",
data: currencyData.datasets[2].data,
backgroundColor: '#4dde8080', // Added transparency
stack: 'stack0'
},
]
},
options: chartOptions
}
);
}
</script>
{% else %}
<c-msg.empty title="{% translate "No information to display" %}"></c-msg.empty>
{% endif %}

View File

@@ -2,13 +2,14 @@
{% load crispy_forms_tags %}
<form _="install init_tom_select
on change trigger updated" id="category-form">
on change trigger updated
init trigger updated" id="category-form">
{% crispy category_form %}
</form>
<div class="row row-cols-1 row-cols-lg-2 gx-3 gy-3">
<div class="col">
<div class="card">
<div class="card h-100">
<div class="card-header">
{% trans "Income/Expense by Account" %}
</div>
@@ -20,14 +21,13 @@
</div>
</div>
<div class="col">
<div class="card">
<div class="card h-100">
<div class="card-header">
{% trans "Income/Expense by Currency" %}
</div>
<div class="card-body">
<div id="currency-card" class="show-loading" hx-get="{% url 'category_sum_by_currency' %}"
hx-trigger="updated from:window" hx-include="#category-form, #picker-form, #picker-type">
</div>
</div>
</div>

View File

@@ -0,0 +1,18 @@
{% load i18n %}
<div hx-get="{% url 'insights_late_transactions' %}" hx-trigger="updated from:window" class="show-loading"
id="transactions-list" hx-swap="outerHTML">
{% if transactions %}
{% for transaction in transactions %}
<c-transaction.item :transaction="transaction"></c-transaction.item>
{% endfor %}
{# Floating bar #}
<c-ui.transactions-action-bar></c-ui.transactions-action-bar>
{% else %}
<c-msg.empty
icon="fa-regular fa-hourglass"
title="{% translate 'All good!' %}"
subtitle="{% translate "No late transactions" %}"></c-msg.empty>
{% endif %}
</div>

View File

@@ -0,0 +1,16 @@
{% load i18n %}
<div hx-get="{% url 'insights_late_transactions' %}" hx-trigger="updated from:window" class="show-loading"
id="transactions-list" hx-swap="outerHTML">
{% if transactions %}
{% for transaction in transactions %}
<c-transaction.item :transaction="transaction"></c-transaction.item>
{% endfor %}
{# Floating bar #}
<c-ui.transactions-action-bar></c-ui.transactions-action-bar>
{% else %}
<c-msg.empty
title="{% translate 'No recent transactions' %}"></c-msg.empty>
{% endif %}
</div>

View File

@@ -7,7 +7,7 @@
<div class="show-loading" hx-get="{% url 'insights_sankey_by_currency' %}" hx-trigger="updated from:window"
hx-swap="outerHTML" hx-include="#picker-form, #picker-type">
{% endif %}
<div class="chart-container position-relative tw-min-h-[60vh] tw-max-h-[60vh] tw-h-full tw-w-full"
<div class="chart-container position-relative tw-min-h-[85vh] tw-max-h-[85vh] tw-h-full tw-w-full"
id="sankeyContainer"
_="init call setupSankeyChart() end">
<canvas id="sankeyChart"></canvas>
@@ -64,6 +64,7 @@
alpha: 0.5,
size: 'max',
color: "white",
nodePadding: 30,
priority: data.nodes.reduce((acc, node) => {
acc[node.id] = node.priority;
return acc;
@@ -77,6 +78,9 @@
options: {
responsive: true,
maintainAspectRatio: false,
layout: {
padding: 20
},
plugins: {
tooltip: {
callbacks: {

View File

@@ -4,11 +4,12 @@
{% block content %}
<div class="container-fluid">
<div class="row my-3">
<div class="row my-3 h-100">
<div class="col-lg-2 col-md-3 mb-3 mb-md-0">
<div class="">
<div class="mb-2 w-100 d-lg-inline-flex d-grid gap-2 flex-wrap justify-content-lg-center" role="group"
_="on change
<div class="position-sticky tw-top-3">
<div class="">
<div class="mb-2 w-100 d-lg-inline-flex d-grid gap-2 flex-wrap justify-content-lg-center" role="group"
_="on change
set type to event.target.value
add .tw-hidden to <#picker-form > div:not(.tw-hidden)/>
@@ -28,65 +29,85 @@
remove .tw-hidden from #date-range-form
end
then trigger updated"
id="picker-type">
<input type="radio" class="btn-check" name="type" value="month" id="monthradio" autocomplete="off" checked>
<label class="btn btn-sm btn-outline-primary flex-grow-1" for="monthradio">{% translate 'Month' %}</label>
id="picker-type">
<input type="radio" class="btn-check" name="type" value="month" id="monthradio" autocomplete="off"
checked>
<label class="btn btn-sm btn-outline-primary flex-grow-1" for="monthradio">{% translate 'Month' %}</label>
<input type="radio" class="btn-check" name="type" value="year" id="yearradio" autocomplete="off">
<label class="btn btn-sm btn-outline-primary flex-grow-1" for="yearradio">{% translate 'Year' %}</label>
<input type="radio" class="btn-check" name="type" value="year" id="yearradio" autocomplete="off">
<label class="btn btn-sm btn-outline-primary flex-grow-1" for="yearradio">{% translate 'Year' %}</label>
<input type="radio" class="btn-check" name="type" value="month-range" id="monthrangeradio" autocomplete="off">
<label class="btn btn-sm btn-outline-primary flex-grow-1" for="monthrangeradio">{% translate 'Month Range' %}</label>
<input type="radio" class="btn-check" name="type" value="month-range" id="monthrangeradio"
autocomplete="off">
<label class="btn btn-sm btn-outline-primary flex-grow-1"
for="monthrangeradio">{% translate 'Month Range' %}</label>
<input type="radio" class="btn-check" name="type" value="year-range" id="yearrangeradio" autocomplete="off">
<label class="btn btn-sm btn-outline-primary flex-grow-1" for="yearrangeradio">{% translate 'Year Range' %}</label>
<input type="radio" class="btn-check" name="type" value="year-range" id="yearrangeradio"
autocomplete="off">
<label class="btn btn-sm btn-outline-primary flex-grow-1"
for="yearrangeradio">{% translate 'Year Range' %}</label>
<input type="radio" class="btn-check" name="type" value="date-range" id="daterangeradio" autocomplete="off">
<label class="btn btn-sm btn-outline-primary flex-grow-1" for="daterangeradio">{% translate 'Date Range' %}</label>
</div>
<form id="picker-form"
_="install init_datepicker
<input type="radio" class="btn-check" name="type" value="date-range" id="daterangeradio"
autocomplete="off">
<label class="btn btn-sm btn-outline-primary flex-grow-1"
for="daterangeradio">{% translate 'Date Range' %}</label>
</div>
<form id="picker-form"
_="install init_datepicker
on change trigger updated">
<div id="month-form" class="">
{% crispy month_form %}
</div>
<div id="year-form" class="tw-hidden">
{% crispy year_form %}
</div>
<div id="month-range-form" class="tw-hidden">
{% crispy month_range_form %}
</div>
<div id="year-range-form" class="tw-hidden">
{% crispy year_range_form %}
</div>
<div id="date-range-form" class="tw-hidden">
{% crispy date_range_form %}
</div>
</form>
</div>
<hr class="mt-0">
<div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist"
aria-orientation="vertical">
<button class="nav-link" id="v-pills-tab" data-bs-toggle="pill" data-bs-target="#v-pills-content"
type="button" role="tab" aria-controls="v-pills-content" aria-selected="false"
hx-get="{% url 'insights_sankey_by_account' %}" hx-include="#picker-form, #picker-type"
hx-indicator="#tab-content"
hx-target="#tab-content">{% trans 'Account Flow' %}
</button>
<button class="nav-link" id="v-pills-tab" data-bs-toggle="pill" data-bs-target="#v-pills-content"
type="button" role="tab" aria-controls="v-pills-content" aria-selected="false"
hx-get="{% url 'insights_sankey_by_currency' %}"
hx-include="#picker-form, #picker-type"
hx-indicator="#tab-content"
hx-target="#tab-content">{% trans 'Currency Flow' %}
</button>
<button class="nav-link" id="v-pills-tab" data-bs-toggle="pill" data-bs-target="#v-pills-content"
type="button" role="tab" aria-controls="v-pills-content" aria-selected="false"
hx-get="{% url 'category_explorer_index' %}"
hx-include="#picker-form, #picker-type"
hx-indicator="#tab-content"
hx-target="#tab-content">{% trans 'Category Explorer' %}
</button>
<div id="month-form" class="">
{% crispy month_form %}
</div>
<div id="year-form" class="tw-hidden">
{% crispy year_form %}
</div>
<div id="month-range-form" class="tw-hidden">
{% crispy month_range_form %}
</div>
<div id="year-range-form" class="tw-hidden">
{% crispy year_range_form %}
</div>
<div id="date-range-form" class="tw-hidden">
{% crispy date_range_form %}
</div>
</form>
</div>
<div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist"
aria-orientation="vertical">
<button class="nav-link" id="v-pills-tab" data-bs-toggle="pill" data-bs-target="#v-pills-content"
type="button" role="tab" aria-controls="v-pills-content" aria-selected="false"
hx-get="{% url 'insights_sankey_by_account' %}" hx-include="#picker-form, #picker-type"
hx-indicator="#tab-content"
hx-target="#tab-content">{% trans 'Account Flow' %}
</button>
<button class="nav-link" id="v-pills-tab" data-bs-toggle="pill" data-bs-target="#v-pills-content"
type="button" role="tab" aria-controls="v-pills-content" aria-selected="false"
hx-get="{% url 'insights_sankey_by_currency' %}"
hx-include="#picker-form, #picker-type"
hx-indicator="#tab-content"
hx-target="#tab-content">{% trans 'Currency Flow' %}
</button>
<button class="nav-link" id="v-pills-tab" data-bs-toggle="pill" data-bs-target="#v-pills-content"
type="button" role="tab" aria-controls="v-pills-content" aria-selected="false"
hx-get="{% url 'category_explorer_index' %}"
hx-include="#picker-form, #picker-type"
hx-indicator="#tab-content"
hx-target="#tab-content">{% trans 'Category Explorer' %}
</button>
<hr>
<button class="nav-link" id="v-pills-tab" data-bs-toggle="pill" data-bs-target="#v-pills-content"
type="button" role="tab" aria-controls="v-pills-content" aria-selected="false"
hx-get="{% url 'insights_late_transactions' %}"
hx-indicator="#tab-content"
hx-target="#tab-content">{% trans 'Late Transactions' %}
</button>
<button class="nav-link" id="v-pills-tab" data-bs-toggle="pill" data-bs-target="#v-pills-content"
type="button" role="tab" aria-controls="v-pills-content" aria-selected="false"
hx-get="{% url 'insights_latest_transactions' %}"
hx-indicator="#tab-content"
hx-target="#tab-content">{% trans 'Latest Transactions' %}
</button>
</div>
</div>
</div>
<div class="col-md-9 col-lg-10">

View File

@@ -45,7 +45,7 @@
</div>
</div>
<div class="row">
<div class="tw-cursor-pointer text-primary text-end"
<div class="tw-cursor-pointer text-primary text-end"
_="on click
set from_value to #id_from_currency's value
set to_value to #id_to_currency's value
@@ -58,5 +58,39 @@
<i class="fa-solid fa-rotate me-2"></i><span>{% trans 'Invert' %}</span>
</div>
</div>
<hr>
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
{% for currency, data in rate_map.items %}
<div class="col">
<c-ui.info-card color="yellow" title="{{ currency }}">
{% for rate in data.rates.values %}
<div class="d-flex justify-content-between align-items-baseline mt-2">
<div class="text-end font-monospace">
<div class="tw-text-gray-400">
{# <c-amount.display#}
{# :amount="1"#}
{# :prefix="data.prefix"#}
{# :suffix="data.suffix"#}
{# :decimal_places="data.decimal_places"></c-amount.display>#}
</div>
</div>
<div class="dotted-line flex-grow-1"></div>
{% if currency.income_projected != 0 %}
<div class="text-end font-monospace tw-text-green-400">
<c-amount.display
:amount="rate.rate"
:prefix="rate.prefix"
:suffix="rate.suffix"
:decimal_places="rate.decimal_places"></c-amount.display>
</div>
{% else %}
<div class="text-end font-monospace">-</div>
{% endif %}
</div>
{% endfor %}
</c-ui.info-card>
</div>
{% endfor %}
</div>
</div>
{% endblock %}

View File

@@ -174,7 +174,7 @@
</div>
<div id="search" class="my-3">
<label class="w-100">
<input type="search" class="form-control" placeholder="Buscar" hx-preserve id="quick-search"
<input type="search" class="form-control" placeholder="{% translate 'Search' %}" hx-preserve id="quick-search"
_="on input or search or htmx:afterSwap from window
if my value is empty
trigger toggle on <.transactions-divider-collapse/>

View File

@@ -2,12 +2,14 @@ import AirDatepicker from 'air-datepicker';
import en from 'air-datepicker/locale/en';
import ptBr from 'air-datepicker/locale/pt-BR';
import nl from 'air-datepicker/locale/nl';
import de from 'air-datepicker/locale/de';
import {createPopper} from '@popperjs/core';
const locales = {
'pt': ptBr,
'en': en,
'nl': nl
'nl': nl,
'de': de
};
function isMobileDevice() {
@@ -40,9 +42,10 @@ window.DatePicker = function createDynamicDatePicker(element) {
dateFormat: element.dataset.dateFormat,
timeFormat: element.dataset.timeFormat,
timepicker: element.dataset.timepicker === 'true',
toggleSelected: element.dataset.toggleSelected === 'true',
autoClose: element.dataset.autoClose === 'true',
buttons: element.dataset.clearButton === 'true' ? ['clear', todayButton] : [todayButton],
locale: locales[element.dataset.language],
locale: locales[element.dataset.language] || locales['en'],
onSelect: ({date, formattedDate, datepicker}) => {
const _event = new CustomEvent("change", {
bubbles: true,
@@ -96,7 +99,6 @@ window.DatePicker = function createDynamicDatePicker(element) {
return new AirDatepicker(element, opts);
};
window.MonthYearPicker = function createDynamicDatePicker(element) {
let todayButton = {
content: element.dataset.nowButtonTxt,
@@ -114,9 +116,10 @@ window.MonthYearPicker = function createDynamicDatePicker(element) {
view: 'months',
minView: 'months',
dateFormat: 'MMMM yyyy',
toggleSelected: element.dataset.toggleSelected === 'true',
autoClose: element.dataset.autoClose === 'true',
buttons: element.dataset.clearButton === 'true' ? ['clear', todayButton] : [todayButton],
locale: locales[element.dataset.language],
locale: locales[element.dataset.language] || locales['en'],
onSelect: ({date, formattedDate, datepicker}) => {
const _event = new CustomEvent("change", {
bubbles: true,
@@ -163,8 +166,8 @@ window.MonthYearPicker = function createDynamicDatePicker(element) {
let opts = {...baseOpts, ...positionConfig};
if (element.dataset.value) {
opts["selectedDates"] = [new Date(element.dataset.value + "T00:00:00")];
opts["startDate"] = [new Date(element.dataset.value + "T00:00:00")];
opts["selectedDates"] = [new Date(element.dataset.value + "T00:00:00")];
opts["startDate"] = [new Date(element.dataset.value + "T00:00:00")];
}
return new AirDatepicker(element, opts);
};
@@ -186,9 +189,10 @@ window.YearPicker = function createDynamicDatePicker(element) {
view: 'years',
minView: 'years',
dateFormat: 'yyyy',
toggleSelected: element.dataset.toggleSelected === 'true',
autoClose: element.dataset.autoClose === 'true',
buttons: element.dataset.clearButton === 'true' ? ['clear', todayButton] : [todayButton],
locale: locales[element.dataset.language],
locale: locales[element.dataset.language] || locales['en'],
onSelect: ({date, formattedDate, datepicker}) => {
const _event = new CustomEvent("change", {
bubbles: true,
@@ -235,8 +239,8 @@ window.YearPicker = function createDynamicDatePicker(element) {
let opts = {...baseOpts, ...positionConfig};
if (element.dataset.value) {
opts["selectedDates"] = [new Date(element.dataset.value + "T00:00:00")];
opts["startDate"] = [new Date(element.dataset.value + "T00:00:00")];
opts["selectedDates"] = [new Date(element.dataset.value + "T00:00:00")];
opts["startDate"] = [new Date(element.dataset.value + "T00:00:00")];
}
return new AirDatepicker(element, opts);
};

View File

@@ -12,6 +12,7 @@ django-cotton~=1.2.1
django-pwa~=2.0.1
djangorestframework~=3.15.2
drf-spectacular~=0.27.2
django-import-export~=4.3.5
gunicorn==22.0.0
whitenoise[brotli]==6.6.0