Compare commits

..

35 Commits

Author SHA1 Message Date
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
Herculino Trotta
84c047c5ab Merge pull request #167 from eitchtee/insights
insights
2025-02-16 13:06:03 -03:00
Herculino Trotta
23f5d09bec locale: update locales 2025-02-16 13:05:35 -03:00
Herculino Trotta
2a19075e23 Merge pull request #166
feat(insights): category explorer
2025-02-16 13:03:20 -03:00
Herculino Trotta
7f231175b2 feat(insights): category explorer 2025-02-16 13:03:02 -03:00
Herculino Trotta
062e84f864 Merge pull request #165
fix(insights): sankey diagrams nodes too far from destination
2025-02-16 02:25:45 -03:00
Herculino Trotta
5521eb20bf fix(insights): sankey diagrams nodes too far from destination 2025-02-16 02:25:29 -03:00
45 changed files with 2285 additions and 616 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",

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,47 @@
from import_export import fields, resources, widgets
from apps.accounts.models import Account
from apps.currencies.models import Currency, ExchangeRate, ExchangeRateService
class CurrencyResource(resources.ModelResource):
exchange_currency = fields.Field(
attribute="exchange_currency",
column_name="exchange_currency",
widget=widgets.ForeignKeyWidget(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"),
)
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,26 @@
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
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):
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,124 @@
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,
)
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"
)
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"),
)
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"),
)
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,286 @@
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")
@only_htmx
@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")
# Debug logging
logger.debug(f"Importing {field_name}")
logger.debug(f"Headers: {dataset.headers}")
logger.debug(f"First row: {dataset[0] if len(dataset) > 0 else 'No data'}")
# 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():
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")
for field_name, resource_class in import_order:
if name == field_name:
import_dataset(content, resource_class, field_name)
break
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,11 @@
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

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,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

@@ -8,11 +8,13 @@ from apps.common.widgets.datepicker import (
AirYearPickerInput,
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):
@@ -27,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):
@@ -42,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):
@@ -65,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):
@@ -88,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):
@@ -108,3 +110,22 @@ class DateRangeForm(forms.Form):
css_class="mb-0",
),
)
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):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.disable_csrf = True
self.helper.layout = Layout("category")

View File

@@ -14,4 +14,19 @@ urlpatterns = [
views.sankey_by_currency,
name="insights_sankey_by_currency",
),
path(
"insights/category-explorer/",
views.category_explorer_index,
name="category_explorer_index",
),
path(
"insights/category-explorer/account/",
views.category_sum_by_account,
name="category_sum_by_account",
),
path(
"insights/category-explorer/currency/",
views.category_sum_by_currency,
name="category_sum_by_currency",
),
]

View File

@@ -0,0 +1,169 @@
from django.db.models import Sum, Case, When, F, DecimalField, Value
from django.db.models.functions import Coalesce
from django.utils.translation import gettext_lazy as _
def get_category_sums_by_account(queryset, category=None):
"""
Returns income/expense sums per account for a specific category.
"""
sums = (
queryset.filter(category=category)
.values("account__name")
.annotate(
current_income=Coalesce(
Sum(
Case(
When(type="IN", then="amount"),
When(is_paid=True, 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),
),
current_expense=Coalesce(
Sum(
Case(
When(type="EX", then=-F("amount")),
When(is_paid=True, 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_income=Coalesce(
Sum(
Case(
When(type="IN", then="amount"),
When(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", then=-F("amount")),
When(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),
),
)
.order_by("account__name")
)
return {
"labels": [item["account__name"] for item in sums],
"datasets": [
{
"label": _("Current Income"),
"data": [float(item["current_income"]) 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=None):
"""
Returns income/expense sums per currency for a specific category.
"""
sums = (
queryset.filter(category=category)
.values("account__currency__name")
.annotate(
current_income=Coalesce(
Sum(
Case(
When(type="IN", then="amount"),
When(is_paid=True, 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),
),
current_expense=Coalesce(
Sum(
Case(
When(type="EX", then=-F("amount")),
When(is_paid=True, 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_income=Coalesce(
Sum(
Case(
When(type="IN", then="amount"),
When(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", then=-F("amount")),
When(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),
),
)
.order_by("account__currency__name")
)
return {
"labels": [item["account__currency__name"] for item in sums],
"datasets": [
{
"label": _("Current Income"),
"data": [float(item["current_income"]) 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

@@ -52,13 +52,29 @@ def generate_sankey_data_by_account(transactions_queryset):
total_volume_by_currency.get(currency, Decimal("0")) + amount
)
unique_accounts = {
account_id: idx
for idx, account_id in enumerate(
transactions_queryset.values_list("account", flat=True).distinct()
)
}
def get_node_priority(node_id: str) -> int:
"""Get priority based on the account ID embedded in the node ID."""
account_id = int(node_id.split("_")[-1])
return unique_accounts[account_id]
def get_node_id(node_type: str, name: str, account_id: int) -> str:
"""Generate unique node ID."""
return f"{node_type}_{name}_{account_id}".lower().replace(" ", "_")
def add_node(node_id: str, display_name: str) -> None:
"""Add node with both ID and display name."""
nodes[node_id] = {"id": node_id, "name": display_name}
"""Add node with ID, display name and priority."""
nodes[node_id] = {
"id": node_id,
"name": display_name,
"priority": get_node_priority(node_id),
}
def add_flow(
from_node_id: str, to_node_id: str, amount: Decimal, currency, is_income: bool
@@ -167,13 +183,29 @@ def generate_sankey_data_by_currency(transactions_queryset):
total_volume_by_currency.get(currency, Decimal("0")) + amount
)
unique_currencies = {
currency_id: idx
for idx, currency_id in enumerate(
transactions_queryset.values_list("account__currency", flat=True).distinct()
)
}
def get_node_priority(node_id: str) -> int:
"""Get priority based on the currency ID embedded in the node ID."""
currency_id = int(node_id.split("_")[-1])
return unique_currencies[currency_id]
def get_node_id(node_type: str, name: str, currency_id: int) -> str:
"""Generate unique node ID including currency information."""
return f"{node_type}_{name}_{currency_id}".lower().replace(" ", "_")
def add_node(node_id: str, display_name: str) -> None:
"""Add node with both ID and display name."""
nodes[node_id] = {"id": node_id, "name": display_name}
"""Add node with ID, display name and priority."""
nodes[node_id] = {
"id": node_id,
"name": display_name,
"priority": get_node_priority(node_id),
}
def add_flow(
from_node_id: str, to_node_id: str, amount: Decimal, currency, is_income: bool

View File

@@ -1,24 +1,28 @@
from dateutil.relativedelta import relativedelta
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
from django.utils import timezone
from django.views.decorators.http import require_http_methods
from dateutil.relativedelta import relativedelta
from apps.transactions.models import Transaction
from apps.insights.utils.sankey import (
generate_sankey_data_by_account,
generate_sankey_data_by_currency,
)
from apps.common.decorators.htmx import only_htmx
from apps.insights.forms import (
SingleMonthForm,
SingleYearForm,
MonthRangeForm,
YearRangeForm,
DateRangeForm,
CategoryForm,
)
from apps.insights.utils.category_explorer import (
get_category_sums_by_account,
get_category_sums_by_currency,
)
from apps.insights.utils.sankey import (
generate_sankey_data_by_account,
generate_sankey_data_by_currency,
)
from apps.common.decorators.htmx import only_htmx
from apps.insights.utils.transactions import get_transactions
from apps.transactions.models import TransactionCategory
@login_required
@@ -61,7 +65,7 @@ def index(request):
@only_htmx
@login_required
@require_http_methods(["GET", "POST"])
@require_http_methods(["GET"])
def sankey_by_account(request):
# Get filtered transactions
@@ -79,7 +83,7 @@ def sankey_by_account(request):
@only_htmx
@login_required
@require_http_methods(["GET", "POST"])
@require_http_methods(["GET"])
def sankey_by_currency(request):
# Get filtered transactions
transactions = get_transactions(request)
@@ -92,3 +96,64 @@ def sankey_by_currency(request):
"insights/fragments/sankey.html",
{"sankey_data": sankey_data, "type": "currency"},
)
@only_htmx
@login_required
@require_http_methods(["GET"])
def category_explorer_index(request):
category_form = CategoryForm()
return render(
request,
"insights/fragments/category_explorer/index.html",
{"category_form": category_form},
)
@only_htmx
@login_required
@require_http_methods(["GET"])
def category_sum_by_account(request):
# Get filtered transactions
transactions = get_transactions(request, include_silent=True)
category = request.GET.get("category")
if category:
category = TransactionCategory.objects.get(id=category)
# Generate data
account_data = get_category_sums_by_account(transactions, category)
else:
account_data = get_category_sums_by_account(transactions, category=None)
return render(
request,
"insights/fragments/category_explorer/charts/account.html",
{"account_data": account_data},
)
@only_htmx
@login_required
@require_http_methods(["GET"])
def category_sum_by_currency(request):
# Get filtered transactions
transactions = get_transactions(request, include_silent=True)
category = request.GET.get("category")
if category:
category = TransactionCategory.objects.get(id=category)
# Generate data
currency_data = get_category_sums_by_currency(transactions, category)
else:
currency_data = get_category_sums_by_currency(transactions, category=None)
return render(
request,
"insights/fragments/category_explorer/charts/currency.html",
{"currency_data": currency_data},
)

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

@@ -2,13 +2,13 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-02-16 00:03-0300\n"
"POT-Creation-Date: 2025-02-19 13:44-0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -26,10 +26,10 @@ msgstr ""
#: 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 ""
@@ -38,10 +38,10 @@ msgstr ""
#: 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
@@ -69,21 +69,22 @@ msgid "New balance"
msgstr ""
#: apps/accounts/forms.py:119 apps/dca/forms.py:85 apps/dca/forms.py:92
#: 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/models.py:378
#: apps/transactions/models.py:558
#: 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: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 ""
#: 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
@@ -154,13 +155,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 ""
#: 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
@@ -331,12 +333,12 @@ msgstr ""
msgid "Cache cleared successfully"
msgstr ""
#: 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 ""
#: apps/common/widgets/datepicker.py:123
#: apps/common/widgets/datepicker.py:139
msgid "Now"
msgstr ""
@@ -365,7 +367,7 @@ msgstr ""
#: 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
@@ -385,7 +387,8 @@ msgstr ""
msgid "Decimal Places"
msgstr ""
#: 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
@@ -415,7 +418,8 @@ msgstr ""
msgid "Date and Time"
msgstr ""
#: 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"
@@ -566,11 +570,11 @@ msgstr ""
msgid "Create transaction"
msgstr ""
#: apps/dca/forms.py:70 apps/transactions/forms.py:266
#: apps/dca/forms.py:70 apps/transactions/forms.py:267
msgid "From Account"
msgstr ""
#: apps/dca/forms.py:76 apps/transactions/forms.py:271
#: apps/dca/forms.py:76 apps/transactions/forms.py:272
msgid "To Account"
msgstr ""
@@ -595,7 +599,7 @@ msgstr ""
msgid "You must provide an account."
msgstr ""
#: 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 ""
@@ -614,7 +618,7 @@ msgstr ""
#: 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 ""
@@ -623,7 +627,7 @@ msgstr ""
msgid "DCA Strategy"
msgstr ""
#: apps/dca/models.py:33
#: apps/dca/models.py:33 apps/export_app/forms.py:145
msgid "DCA Strategies"
msgstr ""
@@ -643,7 +647,7 @@ msgstr ""
msgid "DCA Entry"
msgstr ""
#: apps/dca/models.py:185
#: apps/dca/models.py:185 apps/export_app/forms.py:146
msgid "DCA Entries"
msgstr ""
@@ -671,6 +675,117 @@ msgstr ""
msgid "Entry deleted successfully"
msgstr ""
#: 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 ""
#: 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 ""
#: 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 ""
#: 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 ""
#: 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 ""
#: 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 ""
#: 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 ""
#: apps/export_app/forms.py:80 templates/cotton/transaction/item.html:56
msgid "DCA"
msgstr ""
#: 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 ""
#: apps/export_app/forms.py:112 templates/export_app/fragments/export.html:5
#: templates/export_app/pages/index.html:15
msgid "Export"
msgstr ""
#: apps/export_app/forms.py:121
msgid "Import a ZIP file exported from WYGIWYH"
msgstr ""
#: apps/export_app/forms.py:122
msgid "ZIP File"
msgstr ""
#: apps/export_app/forms.py:138 apps/rules/models.py:16
msgid "Transaction rules"
msgstr ""
#: apps/export_app/forms.py:140 apps/rules/models.py:53
msgid "Edit transaction action"
msgstr ""
#: apps/export_app/forms.py:143 apps/rules/models.py:290
msgid "Update or create transaction actions"
msgstr ""
#: 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 ""
#: apps/export_app/forms.py:187
msgid "Please upload either a ZIP file or at least one CSV file"
msgstr ""
#: apps/export_app/views.py:169
msgid "You have to select at least one export"
msgstr ""
#: apps/export_app/views.py:187
msgid "Data restored successfully"
msgstr ""
#: apps/export_app/views.py:199
msgid ""
"There was an error restoring your data. Check the logs for more details."
msgstr ""
#: apps/import_app/forms.py:49
msgid "Select a file"
msgstr ""
@@ -745,12 +860,49 @@ msgstr ""
msgid "Run deleted successfully"
msgstr ""
#: apps/insights/utils/sankey.py:37 apps/insights/utils/sankey.py:154
#: apps/insights/forms.py:119 apps/insights/utils/sankey.py:36
#: apps/insights/utils/sankey.py:167
msgid "Uncategorized"
msgstr ""
#: apps/insights/utils/sankey.py:118 apps/insights/utils/sankey.py:119
#: apps/insights/utils/sankey.py:234 apps/insights/utils/sankey.py:235
#: apps/insights/utils/category_explorer.py:70
#: apps/insights/utils/category_explorer.py:153
#: templates/cotton/ui/percentage_distribution.html:10
#: templates/cotton/ui/percentage_distribution.html:14
#: templates/insights/fragments/category_explorer/charts/account.html:60
#: templates/insights/fragments/category_explorer/charts/currency.html:60
msgid "Current Income"
msgstr ""
#: apps/insights/utils/category_explorer.py:74
#: apps/insights/utils/category_explorer.py:157
#: 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 ""
#: apps/insights/utils/category_explorer.py:78
#: apps/insights/utils/category_explorer.py:161
#: templates/cotton/ui/percentage_distribution.html:3
#: templates/cotton/ui/percentage_distribution.html:7
#: templates/insights/fragments/category_explorer/charts/account.html:72
#: templates/insights/fragments/category_explorer/charts/currency.html:72
msgid "Projected Income"
msgstr ""
#: apps/insights/utils/category_explorer.py:82
#: apps/insights/utils/category_explorer.py:165
#: templates/cotton/ui/percentage_distribution.html:17
#: templates/cotton/ui/percentage_distribution.html:21
#: templates/insights/fragments/category_explorer/charts/account.html:78
#: templates/insights/fragments/category_explorer/charts/currency.html:78
msgid "Projected Expenses"
msgstr ""
#: 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"
msgstr ""
@@ -770,7 +922,7 @@ msgstr ""
msgid "Set field"
msgstr ""
#: apps/rules/forms.py:65 templates/insights/fragments/sankey.html:84
#: apps/rules/forms.py:65 templates/insights/fragments/sankey.html:94
msgid "To"
msgstr ""
@@ -802,8 +954,8 @@ msgid "Paid"
msgstr ""
#: 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"
@@ -811,13 +963,13 @@ msgstr ""
#: 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:85
#: apps/transactions/models.py:551 templates/insights/fragments/sankey.html:95
msgid "Amount"
msgstr ""
#: 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 ""
@@ -832,16 +984,6 @@ msgstr ""
msgid "Internal ID"
msgstr ""
#: 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 ""
#: apps/rules/forms.py:199
msgid "Search Criteria"
msgstr ""
@@ -858,10 +1000,6 @@ msgstr ""
msgid "Transaction rule"
msgstr ""
#: apps/rules/models.py:16
msgid "Transaction rules"
msgstr ""
#: apps/rules/models.py:40 apps/rules/models.py:78
msgid "Rule"
msgstr ""
@@ -874,10 +1012,6 @@ msgstr ""
msgid "Value"
msgstr ""
#: apps/rules/models.py:53
msgid "Edit transaction action"
msgstr ""
#: apps/rules/models.py:54
msgid "Edit transaction actions"
msgstr ""
@@ -932,10 +1066,6 @@ msgstr ""
msgid "Update or create transaction action"
msgstr ""
#: apps/rules/models.py:290
msgid "Update or create transaction actions"
msgstr ""
#: apps/rules/views.py:52
msgid "Rule deactivated successfully"
msgstr ""
@@ -991,11 +1121,6 @@ msgstr ""
msgid "Transaction Type"
msgstr ""
#: 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 ""
#: apps/transactions/filters.py:91
msgid "Date from"
msgstr ""
@@ -1016,40 +1141,40 @@ msgstr ""
msgid "Amount max"
msgstr ""
#: apps/transactions/forms.py:158
#: apps/transactions/forms.py:160
msgid "More"
msgstr ""
#: apps/transactions/forms.py:278
#: apps/transactions/forms.py:279
msgid "From Amount"
msgstr ""
#: apps/transactions/forms.py:283
#: apps/transactions/forms.py:284
msgid "To Amount"
msgstr ""
#: apps/transactions/forms.py:398
#: apps/transactions/forms.py:399
#: templates/cotton/ui/quick_transactions_buttons.html:40
msgid "Transfer"
msgstr ""
#: apps/transactions/forms.py:610
#: apps/transactions/forms.py:611
msgid "Tag name"
msgstr ""
#: apps/transactions/forms.py:642
#: apps/transactions/forms.py:643
msgid "Entity name"
msgstr ""
#: apps/transactions/forms.py:674
#: apps/transactions/forms.py:675
msgid "Category name"
msgstr ""
#: apps/transactions/forms.py:676
#: apps/transactions/forms.py:677
msgid "Muted categories won't count towards your monthly total"
msgstr ""
#: apps/transactions/forms.py:846
#: apps/transactions/forms.py:847
msgid "End date should be after the start date"
msgstr ""
@@ -1129,14 +1254,6 @@ msgstr ""
msgid "Transaction"
msgstr ""
#: 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 ""
#: apps/transactions/models.py:323 templates/tags/fragments/table.html:53
msgid "No tags"
msgstr ""
@@ -1194,12 +1311,6 @@ msgstr ""
msgid "Installment Amount"
msgstr ""
#: 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 ""
#: apps/transactions/models.py:533
msgid "day(s)"
msgstr ""
@@ -1237,12 +1348,6 @@ msgstr ""
msgid "Last Generated Reference Date"
msgstr ""
#: 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 ""
#: apps/transactions/validators.py:8
#, python-format
msgid "%(value)s has too many decimal places. Maximum is 30."
@@ -1771,20 +1876,11 @@ msgstr ""
msgid "Select"
msgstr ""
#: 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 ""
#: templates/cotton/transaction/item.html:158
#: templates/cotton/ui/deleted_transactions_action_bar.html:47
msgid "Restore"
msgstr ""
#: templates/cotton/ui/account_card.html:15
#: templates/cotton/ui/currency_card.html:10
msgid "projected income"
@@ -1888,26 +1984,6 @@ msgstr ""
msgid "Count"
msgstr ""
#: templates/cotton/ui/percentage_distribution.html:3
#: templates/cotton/ui/percentage_distribution.html:7
msgid "Projected Income"
msgstr ""
#: templates/cotton/ui/percentage_distribution.html:10
#: templates/cotton/ui/percentage_distribution.html:14
msgid "Current Income"
msgstr ""
#: templates/cotton/ui/percentage_distribution.html:17
#: templates/cotton/ui/percentage_distribution.html:21
msgid "Projected Expenses"
msgstr ""
#: templates/cotton/ui/percentage_distribution.html:24
#: templates/cotton/ui/percentage_distribution.html:28
msgid "Current Expenses"
msgstr ""
#: templates/cotton/ui/quick_transactions_buttons.html:25
msgid "Installment"
msgstr ""
@@ -2108,12 +2184,6 @@ msgstr ""
msgid "Page navigation"
msgstr ""
#: 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 ""
#: templates/exchange_rates_services/fragments/list.html:21
msgid "Fetch all"
msgstr ""
@@ -2142,6 +2212,10 @@ msgstr ""
msgid "No services configured"
msgstr ""
#: templates/export_app/pages/index.html:4 templates/includes/navbar.html:136
msgid "Export and Restore"
msgstr ""
#: templates/import_app/fragments/profiles/add.html:6
msgid "Add new import profile"
msgstr ""
@@ -2154,11 +2228,6 @@ msgstr ""
msgid "Edit import profile"
msgstr ""
#: templates/import_app/fragments/profiles/list.html:5
#: templates/import_app/pages/profiles_index.html:4
msgid "Import Profiles"
msgstr ""
#: templates/import_app/fragments/profiles/list.html:17
msgid "New"
msgstr ""
@@ -2283,20 +2352,15 @@ msgstr ""
msgid "Automation"
msgstr ""
#: templates/includes/navbar.html:132 templates/rules/fragments/list.html:5
#: templates/rules/pages/index.html:4
msgid "Rules"
msgstr ""
#: templates/includes/navbar.html:146
#: templates/includes/navbar.html:148
msgid "Only use this if you know what you're doing"
msgstr ""
#: templates/includes/navbar.html:147
#: templates/includes/navbar.html:149
msgid "Django Admin"
msgstr ""
#: templates/includes/navbar.html:156
#: templates/includes/navbar.html:158
msgid "Calculator"
msgstr ""
@@ -2328,44 +2392,69 @@ msgstr ""
msgid "Confirm"
msgstr ""
#: templates/insights/fragments/sankey.html:83
#: templates/insights/fragments/category_explorer/charts/account.html:99
#: templates/insights/fragments/category_explorer/charts/currency.html:91
#: 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 ""
#: templates/insights/fragments/category_explorer/index.html:14
msgid "Income/Expense by Account"
msgstr ""
#: templates/insights/fragments/category_explorer/index.html:26
msgid "Income/Expense by Currency"
msgstr ""
#: templates/insights/fragments/sankey.html:93
msgid "From"
msgstr ""
#: templates/insights/fragments/sankey.html:86
#: templates/insights/fragments/sankey.html:96
msgid "Percentage"
msgstr ""
#: templates/insights/pages/index.html:33
#: templates/insights/pages/index.html:35
msgid "Month"
msgstr ""
#: 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 ""
#: templates/insights/pages/index.html:39
#: templates/insights/pages/index.html:43
msgid "Month Range"
msgstr ""
#: templates/insights/pages/index.html:42
#: templates/insights/pages/index.html:48
msgid "Year Range"
msgstr ""
#: templates/insights/pages/index.html:45
#: templates/insights/pages/index.html:53
msgid "Date Range"
msgstr ""
#: templates/insights/pages/index.html:74
#: templates/insights/pages/index.html:82
msgid "Account Flow"
msgstr ""
#: templates/insights/pages/index.html:81
#: templates/insights/pages/index.html:89
msgid "Currency Flow"
msgstr ""
#: templates/insights/pages/index.html:96
msgid "Category Explorer"
msgstr ""
#: templates/installment_plans/fragments/add.html:5
msgid "Add installment plan"
msgstr ""
@@ -2433,17 +2522,6 @@ msgstr ""
msgid "No transactions this month"
msgstr ""
#: 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 ""
#: templates/monthly_overview/fragments/monthly_summary.html:6
msgid "Daily Spending Allowance"
msgstr ""

View File

@@ -2,13 +2,13 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-02-16 00:03-0300\n"
"POT-Creation-Date: 2025-02-19 13:44-0300\n"
"PO-Revision-Date: 2025-02-12 06:58+0100\n"
"Last-Translator: Dimitri Decrock <dimitri@fam-decrock.eu>\n"
"Language-Team: \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/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/models.py:378
#: apps/transactions/models.py:558
#: 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: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"
@@ -583,11 +587,11 @@ msgstr "Diensten succesvol in de wachtrij geplaatst"
msgid "Create transaction"
msgstr "Verwijderde 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"
@@ -614,7 +618,7 @@ msgstr "Bewerk verrichting"
msgid "You must provide an account."
msgstr ""
#: 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."
@@ -634,7 +638,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 +647,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 +667,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 +695,121 @@ 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 ""
#: 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
#, fuzzy
#| msgid "Import"
msgid "Export"
msgstr "Importeer"
#: apps/export_app/forms.py:121
msgid "Import a ZIP file exported from WYGIWYH"
msgstr ""
#: apps/export_app/forms.py:122
msgid "ZIP File"
msgstr ""
#: 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 ""
#: apps/export_app/views.py:169
msgid "You have to select at least one export"
msgstr ""
#: apps/export_app/views.py:187
#, fuzzy
#| msgid "Tag updated successfully"
msgid "Data restored successfully"
msgstr "Label succesvol bijgewerkt"
#: apps/export_app/views.py:199
msgid ""
"There was an error restoring your data. Check the logs for more details."
msgstr ""
#: apps/import_app/forms.py:49
msgid "Select a file"
msgstr "Selecteer een bestand"
@@ -765,14 +884,51 @@ msgstr "Importrun met succes in de wachtrij geplaatst"
msgid "Run deleted successfully"
msgstr "Run met succes verwijderd"
#: apps/insights/utils/sankey.py:37 apps/insights/utils/sankey.py:154
#: apps/insights/forms.py:119 apps/insights/utils/sankey.py:36
#: apps/insights/utils/sankey.py:167
#, fuzzy
#| msgid "Categories"
msgid "Uncategorized"
msgstr "Categorieën"
#: apps/insights/utils/sankey.py:118 apps/insights/utils/sankey.py:119
#: apps/insights/utils/sankey.py:234 apps/insights/utils/sankey.py:235
#: apps/insights/utils/category_explorer.py:70
#: apps/insights/utils/category_explorer.py:153
#: templates/cotton/ui/percentage_distribution.html:10
#: templates/cotton/ui/percentage_distribution.html:14
#: templates/insights/fragments/category_explorer/charts/account.html:60
#: templates/insights/fragments/category_explorer/charts/currency.html:60
msgid "Current Income"
msgstr "Huidige inkomsten"
#: apps/insights/utils/category_explorer.py:74
#: apps/insights/utils/category_explorer.py:157
#: 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:78
#: apps/insights/utils/category_explorer.py:161
#: templates/cotton/ui/percentage_distribution.html:3
#: templates/cotton/ui/percentage_distribution.html:7
#: templates/insights/fragments/category_explorer/charts/account.html:72
#: templates/insights/fragments/category_explorer/charts/currency.html:72
msgid "Projected Income"
msgstr "Verwachte inkomsten"
#: apps/insights/utils/category_explorer.py:82
#: apps/insights/utils/category_explorer.py:165
#: templates/cotton/ui/percentage_distribution.html:17
#: templates/cotton/ui/percentage_distribution.html:21
#: templates/insights/fragments/category_explorer/charts/account.html:78
#: templates/insights/fragments/category_explorer/charts/currency.html:78
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"
@@ -794,7 +950,7 @@ msgstr "Als..."
msgid "Set field"
msgstr "Veld instellen"
#: apps/rules/forms.py:65 templates/insights/fragments/sankey.html:84
#: apps/rules/forms.py:65 templates/insights/fragments/sankey.html:94
msgid "To"
msgstr "Naar"
@@ -826,8 +982,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"
@@ -835,13 +991,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:85
#: 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"
@@ -856,16 +1012,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"
@@ -882,10 +1028,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"
@@ -898,10 +1040,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"
@@ -959,10 +1097,6 @@ msgstr ""
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"
@@ -1018,11 +1152,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"
@@ -1043,40 +1172,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"
@@ -1162,14 +1291,6 @@ 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"
@@ -1231,12 +1352,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)"
@@ -1274,12 +1389,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."
@@ -1808,20 +1917,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"
@@ -1925,26 +2025,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"
@@ -2145,12 +2225,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"
@@ -2179,6 +2253,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 ""
#: templates/import_app/fragments/profiles/add.html:6
msgid "Add new import profile"
msgstr "Nieuw importprofiel toevoegen"
@@ -2191,11 +2269,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"
@@ -2321,20 +2394,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"
@@ -2368,54 +2436,83 @@ msgstr "Annuleer"
msgid "Confirm"
msgstr "Bevestig"
#: templates/insights/fragments/sankey.html:83
#: templates/insights/fragments/category_explorer/charts/account.html:99
#: templates/insights/fragments/category_explorer/charts/currency.html:91
#: 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 ""
#: templates/insights/fragments/category_explorer/index.html:26
#, fuzzy
#| msgid "Exchange Currency"
msgid "Income/Expense by Currency"
msgstr "Eenheid Wisselgeld"
#: templates/insights/fragments/sankey.html:93
msgid "From"
msgstr ""
#: templates/insights/fragments/sankey.html:86
#: templates/insights/fragments/sankey.html:96
msgid "Percentage"
msgstr ""
#: templates/insights/pages/index.html:33
#: templates/insights/pages/index.html:35
#, fuzzy
#| msgid "Monthly"
msgid "Month"
msgstr "Maandelijks"
#: 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
#: templates/insights/pages/index.html:43
#, fuzzy
#| msgid "Unchanged"
msgid "Month Range"
msgstr "Ongewijzigd"
#: templates/insights/pages/index.html:42
#: templates/insights/pages/index.html:48
msgid "Year Range"
msgstr ""
#: templates/insights/pages/index.html:45
#: templates/insights/pages/index.html:53
#, fuzzy
#| msgid "Date and Time"
msgid "Date Range"
msgstr "Datum en Tijd"
#: templates/insights/pages/index.html:74
#: templates/insights/pages/index.html:82
#, fuzzy
#| msgid "Account"
msgid "Account Flow"
msgstr "Rekening"
#: templates/insights/pages/index.html:81
#: templates/insights/pages/index.html:89
#, fuzzy
#| msgid "Currency Code"
msgid "Currency Flow"
msgstr "Munteenheids Code"
#: templates/insights/pages/index.html:96
#, fuzzy
#| msgid "Category name"
msgid "Category Explorer"
msgstr "Naam van categorie"
#: templates/installment_plans/fragments/add.html:5
msgid "Add installment plan"
msgstr "Afbetalingsplan toevoegen"
@@ -2485,17 +2582,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"
@@ -2789,6 +2875,11 @@ msgstr "Bedragen tonen"
msgid "Yearly Overview"
msgstr "Jaaroverzicht"
#, fuzzy
#~| msgid "Installment Plans"
#~ msgid "Installment Planss"
#~ msgstr "Afbetalingsplannen"
#, fuzzy
#~| msgid "Start Date"
#~ msgid "Search Date"
@@ -2879,11 +2970,6 @@ msgstr "Jaaroverzicht"
#~ msgid "Search Category"
#~ msgstr "Categorie"
#, fuzzy
#~| msgid "Category name"
#~ msgid "Category Operator"
#~ msgstr "Naam van categorie"
#, fuzzy
#~| msgid "Search"
#~ msgid "Search Tags"

View File

@@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-02-16 00:03-0300\n"
"PO-Revision-Date: 2025-02-16 00:04-0300\n"
"POT-Creation-Date: 2025-02-19 13:44-0300\n"
"PO-Revision-Date: 2025-02-19 13:50-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/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/models.py:378
#: apps/transactions/models.py:558
#: 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: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:169
msgid "You have to select at least one export"
msgstr "É necessário selecionar pelo menos uma exportação"
#: apps/export_app/views.py:187
msgid "Data restored successfully"
msgstr "Dados restaurados com sucesso"
#: apps/export_app/views.py:199
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,12 +876,49 @@ msgstr "Importação adicionada à fila com sucesso"
msgid "Run deleted successfully"
msgstr "Importação apagada com sucesso"
#: apps/insights/utils/sankey.py:37 apps/insights/utils/sankey.py:154
#: 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/sankey.py:118 apps/insights/utils/sankey.py:119
#: apps/insights/utils/sankey.py:234 apps/insights/utils/sankey.py:235
#: apps/insights/utils/category_explorer.py:70
#: apps/insights/utils/category_explorer.py:153
#: templates/cotton/ui/percentage_distribution.html:10
#: templates/cotton/ui/percentage_distribution.html:14
#: templates/insights/fragments/category_explorer/charts/account.html:60
#: templates/insights/fragments/category_explorer/charts/currency.html:60
msgid "Current Income"
msgstr "Renda Atual"
#: apps/insights/utils/category_explorer.py:74
#: apps/insights/utils/category_explorer.py:157
#: 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:78
#: apps/insights/utils/category_explorer.py:161
#: templates/cotton/ui/percentage_distribution.html:3
#: templates/cotton/ui/percentage_distribution.html:7
#: templates/insights/fragments/category_explorer/charts/account.html:72
#: templates/insights/fragments/category_explorer/charts/currency.html:72
msgid "Projected Income"
msgstr "Renda Prevista"
#: apps/insights/utils/category_explorer.py:82
#: apps/insights/utils/category_explorer.py:165
#: templates/cotton/ui/percentage_distribution.html:17
#: templates/cotton/ui/percentage_distribution.html:21
#: templates/insights/fragments/category_explorer/charts/account.html:78
#: templates/insights/fragments/category_explorer/charts/currency.html:78
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"
msgstr "Salvo"
@@ -784,7 +938,7 @@ msgstr "Se..."
msgid "Set field"
msgstr "Definir campo"
#: apps/rules/forms.py:65 templates/insights/fragments/sankey.html:84
#: apps/rules/forms.py:65 templates/insights/fragments/sankey.html:94
msgid "To"
msgstr "Para"
@@ -816,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"
@@ -825,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:85
#: 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"
@@ -846,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"
@@ -872,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"
@@ -888,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"
@@ -946,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"
@@ -1007,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"
@@ -1032,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"
@@ -1150,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"
@@ -1215,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)"
@@ -1258,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."
@@ -1792,20 +1899,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"
@@ -1909,26 +2007,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"
@@ -2130,12 +2208,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"
@@ -2164,6 +2236,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"
@@ -2176,11 +2252,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"
@@ -2307,20 +2378,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"
@@ -2353,44 +2419,69 @@ msgstr "Cancelar"
msgid "Confirm"
msgstr "Confirmar"
#: templates/insights/fragments/sankey.html:83
#: templates/insights/fragments/category_explorer/charts/account.html:99
#: templates/insights/fragments/category_explorer/charts/currency.html:91
#: 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:26
msgid "Income/Expense by Currency"
msgstr "Gasto/Despesa por Moeda"
#: templates/insights/fragments/sankey.html:93
msgid "From"
msgstr "De"
#: templates/insights/fragments/sankey.html:86
#: 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:82
msgid "Account Flow"
msgstr "Fluxo de Conta"
#: templates/insights/pages/index.html:81
#: templates/insights/pages/index.html:89
msgid "Currency Flow"
msgstr "Fluxo de Moeda"
#: templates/insights/pages/index.html:96
msgid "Category Explorer"
msgstr "Explorador de Categoria"
#: templates/installment_plans/fragments/add.html:5
msgid "Add installment plan"
msgstr "Adicionar parcelamento"
@@ -2460,17 +2551,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"
@@ -2761,6 +2841,11 @@ msgstr "Mostrar valores"
msgid "Yearly Overview"
msgstr "Visão Anual"
#, fuzzy
#~| msgid "Installment Plans"
#~ msgid "Installment Planss"
#~ msgstr "Parcelamentos"
#, fuzzy
#~| msgid "Tags"
#~ msgid "No Tags"
@@ -2856,11 +2941,6 @@ msgstr "Visão Anual"
#~ msgid "Search Category"
#~ msgstr "Categoria"
#, fuzzy
#~| msgid "Category name"
#~ msgid "Category Operator"
#~ msgstr "Nome da Categoria"
#, fuzzy
#~| msgid "Search"
#~ msgid "Search Tags"

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 }}"

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

@@ -0,0 +1,100 @@
{% load i18n %}
{% 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 }};
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 'Current Income' %}",
data: accountData.datasets[0].data,
backgroundColor: '#4dde80',
stack: 'stack0'
},
{
label: "{% trans 'Current Expenses' %}",
data: accountData.datasets[1].data,
backgroundColor: '#f87171',
stack: 'stack0'
},
{
label: "{% trans 'Projected Income' %}",
data: accountData.datasets[2].data,
backgroundColor: '#4dde8080', // Added transparency
stack: 'stack0'
},
{
label: "{% trans 'Projected Expenses' %}",
data: accountData.datasets[3].data,
backgroundColor: '#f8717180', // 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

@@ -0,0 +1,92 @@
{% load i18n %}
{% 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 }};
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 'Current Income' %}",
data: currencyData.datasets[0].data,
backgroundColor: '#4dde80',
stack: 'stack0'
},
{
label: "{% trans 'Current Expenses' %}",
data: currencyData.datasets[1].data,
backgroundColor: '#f87171',
stack: 'stack0'
},
{
label: "{% trans 'Projected Income' %}",
data: currencyData.datasets[2].data,
backgroundColor: '#4dde8080', // Added transparency
stack: 'stack0'
},
{
label: "{% trans 'Projected Expenses' %}",
data: currencyData.datasets[3].data,
backgroundColor: '#f8717180', // Added transparency
stack: 'stack0'
}
]
},
options: chartOptions
}
);
}
</script>
{% else %}
<c-msg.empty title="{% translate "No information to display" %}"></c-msg.empty>
{% endif %}

View File

@@ -0,0 +1,37 @@
{% load i18n %}
{% load crispy_forms_tags %}
<form _="install init_tom_select
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 h-100">
<div class="card-header">
{% trans "Income/Expense by Account" %}
</div>
<div class="card-body">
<div id="account-card" class="show-loading" hx-get="{% url 'category_sum_by_account' %}"
hx-trigger="updated from:window" hx-include="#category-form, #picker-form, #picker-type">
</div>
</div>
</div>
</div>
<div class="col">
<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>
</div>
</div>

View File

@@ -1,15 +1,17 @@
{% load i18n %}
{% if type == 'account' %}
<div class="show-loading" hx-get="{% url 'insights_sankey_by_account' %}" hx-trigger="updated from:window" hx-swap="outerHTML" hx-include="#picker-form, #picker-type">
<div class="show-loading" hx-get="{% url 'insights_sankey_by_account' %}" hx-trigger="updated from:window"
hx-swap="outerHTML" hx-include="#picker-form, #picker-type">
{% else %}
<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">
<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"
id="sankeyContainer"
_="init call setupSankeyChart() end">
<canvas id="sankeyChart"></canvas>
</div>
<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>
</div>
</div>
<script>
@@ -61,7 +63,12 @@
colorMode: 'gradient',
alpha: 0.5,
size: 'max',
color: "white"
color: "white",
nodePadding: 30,
priority: data.nodes.reduce((acc, node) => {
acc[node.id] = node.priority;
return acc;
}, {}),
}]
};
@@ -71,6 +78,9 @@
options: {
responsive: true,
maintainAspectRatio: false,
layout: {
padding: 20
},
plugins: {
tooltip: {
callbacks: {
@@ -98,23 +108,10 @@
}
};
// Destroy existing chart if it exists
const existingChart = Chart.getChart(chartId);
if (existingChart) {
existingChart.destroy();
}
// Create new chart
var chart = new Chart(
new Chart(
document.getElementById(chartId),
config
);
window.addEventListener('resize', () => {
chart.resize();
});
document.addEventListener('fullscreenchange', function () {
console.log('oi');
chart.resize();
});
}
</script>

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,58 +29,73 @@
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>
<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>
</div>
</div>
<div class="col-md-9 col-lg-10">

View File

@@ -40,6 +40,7 @@ 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],
@@ -96,7 +97,6 @@ window.DatePicker = function createDynamicDatePicker(element) {
return new AirDatepicker(element, opts);
};
window.MonthYearPicker = function createDynamicDatePicker(element) {
let todayButton = {
content: element.dataset.nowButtonTxt,
@@ -114,6 +114,7 @@ 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],
@@ -163,8 +164,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,6 +187,7 @@ 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],
@@ -235,8 +237,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