mirror of
https://github.com/eitchtee/WYGIWYH.git
synced 2026-02-25 08:54:52 +01:00
Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8de340b68b | ||
|
|
ef15b85386 | ||
|
|
45d939237d | ||
|
|
6bf262e514 | ||
|
|
f9d9137336 | ||
|
|
b532521f27 | ||
|
|
1e06e2d34d | ||
|
|
a33fa5e184 | ||
|
|
a2453695d8 | ||
|
|
3e929d0433 | ||
|
|
185fc464a5 | ||
|
|
647c009525 | ||
|
|
ba75492dcc | ||
|
|
8312baaf45 | ||
|
|
4d346dc278 | ||
|
|
70ff7fab38 | ||
|
|
6947c6affd | ||
|
|
dcab83f936 | ||
|
|
b228e4ec26 | ||
|
|
4071a1301f | ||
|
|
5c9db10710 | ||
|
|
19c92e0014 | ||
|
|
6459f2eb46 | ||
|
|
7926e081ef | ||
|
|
ceefe7075f | ||
|
|
ad3230fd83 | ||
|
|
c89b07ed93 | ||
|
|
201ccea842 | ||
|
|
32ada488b4 | ||
|
|
794d11a355 | ||
|
|
67f8f5fe89 | ||
|
|
9ac69fd92a | ||
|
|
069f1b450c | ||
|
|
2f388af928 | ||
|
|
beeb0579ce | ||
|
|
a8666da57b | ||
|
|
835316d0f3 | ||
|
|
f5feeb9617 | ||
|
|
09e380a480 | ||
|
|
3080df9b66 | ||
|
|
ebc41a8049 | ||
|
|
635628e30e | ||
|
|
819a58ac06 | ||
|
|
d433375522 | ||
|
|
c0150f71a8 | ||
|
|
6119698d38 | ||
|
|
f5ae231601 | ||
|
|
972d23abbd | ||
|
|
9a514a8a69 | ||
|
|
7325231548 | ||
|
|
570657371a | ||
|
|
67da60b5b0 |
@@ -55,6 +55,7 @@ INSTALLED_APPS = [
|
||||
"hijack",
|
||||
"hijack.contrib.admin",
|
||||
"django_filters",
|
||||
"import_export",
|
||||
"apps.users.apps.UsersConfig",
|
||||
"procrastinate.contrib.django",
|
||||
"apps.transactions.apps.TransactionsConfig",
|
||||
@@ -63,6 +64,7 @@ INSTALLED_APPS = [
|
||||
"apps.common.apps.CommonConfig",
|
||||
"apps.net_worth.apps.NetWorthConfig",
|
||||
"apps.import_app.apps.ImportConfig",
|
||||
"apps.export_app.apps.ExportConfig",
|
||||
"apps.api.apps.ApiConfig",
|
||||
"cachalot",
|
||||
"rest_framework",
|
||||
@@ -161,6 +163,7 @@ AUTH_USER_MODEL = "users.User"
|
||||
|
||||
LANGUAGE_CODE = "en"
|
||||
LANGUAGES = (
|
||||
("de", "Deutsch"),
|
||||
("en", "English"),
|
||||
("nl", "Nederlands"),
|
||||
("pt-br", "Português (Brasil)"),
|
||||
|
||||
@@ -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")),
|
||||
]
|
||||
|
||||
@@ -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):
|
||||
|
||||
0
app/apps/export_app/__init__.py
Normal file
0
app/apps/export_app/__init__.py
Normal file
3
app/apps/export_app/admin.py
Normal file
3
app/apps/export_app/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
6
app/apps/export_app/apps.py
Normal file
6
app/apps/export_app/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ExportConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "apps.export_app"
|
||||
189
app/apps/export_app/forms.py
Normal file
189
app/apps/export_app/forms.py
Normal 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
|
||||
0
app/apps/export_app/migrations/__init__.py
Normal file
0
app/apps/export_app/migrations/__init__.py
Normal file
3
app/apps/export_app/models.py
Normal file
3
app/apps/export_app/models.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
0
app/apps/export_app/resources/__init__.py
Normal file
0
app/apps/export_app/resources/__init__.py
Normal file
26
app/apps/export_app/resources/accounts.py
Normal file
26
app/apps/export_app/resources/accounts.py
Normal 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
|
||||
52
app/apps/export_app/resources/currencies.py
Normal file
52
app/apps/export_app/resources/currencies.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from import_export import fields, resources, widgets
|
||||
|
||||
from apps.accounts.models import Account
|
||||
from apps.currencies.models import Currency, ExchangeRate, ExchangeRateService
|
||||
from apps.export_app.widgets.foreign_key import SkipMissingForeignKeyWidget
|
||||
from apps.export_app.widgets.numbers import UniversalDecimalWidget
|
||||
|
||||
|
||||
class CurrencyResource(resources.ModelResource):
|
||||
exchange_currency = fields.Field(
|
||||
attribute="exchange_currency",
|
||||
column_name="exchange_currency",
|
||||
widget=SkipMissingForeignKeyWidget(Currency, "name"),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Currency
|
||||
|
||||
|
||||
class ExchangeRateResource(resources.ModelResource):
|
||||
from_currency = fields.Field(
|
||||
attribute="from_currency",
|
||||
column_name="from_currency",
|
||||
widget=widgets.ForeignKeyWidget(Currency, "name"),
|
||||
)
|
||||
to_currency = fields.Field(
|
||||
attribute="to_currency",
|
||||
column_name="to_currency",
|
||||
widget=widgets.ForeignKeyWidget(Currency, "name"),
|
||||
)
|
||||
rate = fields.Field(
|
||||
attribute="rate", column_name="rate", widget=UniversalDecimalWidget()
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = ExchangeRate
|
||||
|
||||
|
||||
class ExchangeRateServiceResource(resources.ModelResource):
|
||||
target_currencies = fields.Field(
|
||||
attribute="target_currencies",
|
||||
column_name="target_currencies",
|
||||
widget=widgets.ManyToManyWidget(Currency, field="name"),
|
||||
)
|
||||
target_accounts = fields.Field(
|
||||
attribute="target_accounts",
|
||||
column_name="target_accounts",
|
||||
widget=widgets.ManyToManyWidget(Account, field="name"),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = ExchangeRateService
|
||||
38
app/apps/export_app/resources/dca.py
Normal file
38
app/apps/export_app/resources/dca.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from import_export import fields, resources
|
||||
from import_export.widgets import ForeignKeyWidget
|
||||
|
||||
from apps.dca.models import DCAStrategy, DCAEntry
|
||||
from apps.currencies.models import Currency
|
||||
from apps.export_app.widgets.numbers import UniversalDecimalWidget
|
||||
|
||||
|
||||
class DCAStrategyResource(resources.ModelResource):
|
||||
target_currency = fields.Field(
|
||||
attribute="target_currency",
|
||||
column_name="target_currency",
|
||||
widget=ForeignKeyWidget(Currency, "name"),
|
||||
)
|
||||
payment_currency = fields.Field(
|
||||
attribute="payment_currency",
|
||||
column_name="payment_currency",
|
||||
widget=ForeignKeyWidget(Currency, "name"),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = DCAStrategy
|
||||
|
||||
|
||||
class DCAEntryResource(resources.ModelResource):
|
||||
amount_paid = fields.Field(
|
||||
attribute="amount_paid",
|
||||
column_name="amount_paid",
|
||||
widget=UniversalDecimalWidget(),
|
||||
)
|
||||
amount_received = fields.Field(
|
||||
attribute="amount_received",
|
||||
column_name="amount_received",
|
||||
widget=UniversalDecimalWidget(),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = DCAEntry
|
||||
8
app/apps/export_app/resources/import_app.py
Normal file
8
app/apps/export_app/resources/import_app.py
Normal 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
|
||||
25
app/apps/export_app/resources/rules.py
Normal file
25
app/apps/export_app/resources/rules.py
Normal 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
|
||||
143
app/apps/export_app/resources/transactions.py
Normal file
143
app/apps/export_app/resources/transactions.py
Normal file
@@ -0,0 +1,143 @@
|
||||
from import_export import fields, resources
|
||||
from import_export.widgets import ForeignKeyWidget
|
||||
|
||||
from apps.accounts.models import Account
|
||||
from apps.export_app.widgets.foreign_key import AutoCreateForeignKeyWidget
|
||||
from apps.export_app.widgets.many_to_many import AutoCreateManyToManyWidget
|
||||
from apps.export_app.widgets.string import EmptyStringToNoneField
|
||||
from apps.transactions.models import (
|
||||
Transaction,
|
||||
TransactionCategory,
|
||||
TransactionTag,
|
||||
TransactionEntity,
|
||||
RecurringTransaction,
|
||||
InstallmentPlan,
|
||||
)
|
||||
from apps.export_app.widgets.numbers import UniversalDecimalWidget
|
||||
|
||||
|
||||
class TransactionResource(resources.ModelResource):
|
||||
account = fields.Field(
|
||||
attribute="account",
|
||||
column_name="account",
|
||||
widget=ForeignKeyWidget(Account, "name"),
|
||||
)
|
||||
|
||||
category = fields.Field(
|
||||
attribute="category",
|
||||
column_name="category",
|
||||
widget=AutoCreateForeignKeyWidget(TransactionCategory, "name"),
|
||||
)
|
||||
|
||||
tags = fields.Field(
|
||||
attribute="tags",
|
||||
column_name="tags",
|
||||
widget=AutoCreateManyToManyWidget(TransactionTag, field="name"),
|
||||
)
|
||||
|
||||
entities = fields.Field(
|
||||
attribute="entities",
|
||||
column_name="entities",
|
||||
widget=AutoCreateManyToManyWidget(TransactionEntity, field="name"),
|
||||
)
|
||||
|
||||
internal_id = EmptyStringToNoneField(
|
||||
column_name="internal_id", attribute="internal_id"
|
||||
)
|
||||
|
||||
amount = fields.Field(
|
||||
attribute="amount",
|
||||
column_name="amount",
|
||||
widget=UniversalDecimalWidget(),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Transaction
|
||||
|
||||
def get_queryset(self):
|
||||
return Transaction.all_objects.all()
|
||||
|
||||
|
||||
class TransactionTagResource(resources.ModelResource):
|
||||
class Meta:
|
||||
model = TransactionTag
|
||||
|
||||
|
||||
class TransactionEntityResource(resources.ModelResource):
|
||||
class Meta:
|
||||
model = TransactionEntity
|
||||
|
||||
|
||||
class TransactionCategoyResource(resources.ModelResource):
|
||||
class Meta:
|
||||
model = TransactionCategory
|
||||
|
||||
|
||||
class RecurringTransactionResource(resources.ModelResource):
|
||||
account = fields.Field(
|
||||
attribute="account",
|
||||
column_name="account",
|
||||
widget=ForeignKeyWidget(Account, "name"),
|
||||
)
|
||||
|
||||
category = fields.Field(
|
||||
attribute="category",
|
||||
column_name="category",
|
||||
widget=AutoCreateForeignKeyWidget(TransactionCategory, "name"),
|
||||
)
|
||||
|
||||
tags = fields.Field(
|
||||
attribute="tags",
|
||||
column_name="tags",
|
||||
widget=AutoCreateManyToManyWidget(TransactionTag, field="name"),
|
||||
)
|
||||
|
||||
entities = fields.Field(
|
||||
attribute="entities",
|
||||
column_name="entities",
|
||||
widget=AutoCreateManyToManyWidget(TransactionEntity, field="name"),
|
||||
)
|
||||
|
||||
amount = fields.Field(
|
||||
attribute="amount",
|
||||
column_name="amount",
|
||||
widget=UniversalDecimalWidget(),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = RecurringTransaction
|
||||
|
||||
|
||||
class InstallmentPlanResource(resources.ModelResource):
|
||||
account = fields.Field(
|
||||
attribute="account",
|
||||
column_name="account",
|
||||
widget=ForeignKeyWidget(Account, "name"),
|
||||
)
|
||||
|
||||
category = fields.Field(
|
||||
attribute="category",
|
||||
column_name="category",
|
||||
widget=AutoCreateForeignKeyWidget(TransactionCategory, "name"),
|
||||
)
|
||||
|
||||
tags = fields.Field(
|
||||
attribute="tags",
|
||||
column_name="tags",
|
||||
widget=AutoCreateManyToManyWidget(TransactionTag, field="name"),
|
||||
)
|
||||
|
||||
entities = fields.Field(
|
||||
attribute="entities",
|
||||
column_name="entities",
|
||||
widget=AutoCreateManyToManyWidget(TransactionEntity, field="name"),
|
||||
)
|
||||
|
||||
installment_amount = fields.Field(
|
||||
attribute="installment_amount",
|
||||
column_name="installment_amount",
|
||||
widget=UniversalDecimalWidget(),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = InstallmentPlan
|
||||
3
app/apps/export_app/tests.py
Normal file
3
app/apps/export_app/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
8
app/apps/export_app/urls.py
Normal file
8
app/apps/export_app/urls.py
Normal 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"),
|
||||
]
|
||||
284
app/apps/export_app/views.py
Normal file
284
app/apps/export_app/views.py
Normal file
@@ -0,0 +1,284 @@
|
||||
import logging
|
||||
import zipfile
|
||||
from io import BytesIO, TextIOWrapper
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.db import transaction
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import render
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from tablib import Dataset
|
||||
|
||||
from apps.export_app.forms import ExportForm, RestoreForm
|
||||
from apps.export_app.resources.accounts import AccountResource
|
||||
from apps.export_app.resources.transactions import (
|
||||
TransactionResource,
|
||||
TransactionTagResource,
|
||||
TransactionEntityResource,
|
||||
TransactionCategoyResource,
|
||||
InstallmentPlanResource,
|
||||
RecurringTransactionResource,
|
||||
)
|
||||
from apps.export_app.resources.currencies import (
|
||||
CurrencyResource,
|
||||
ExchangeRateResource,
|
||||
ExchangeRateServiceResource,
|
||||
)
|
||||
from apps.export_app.resources.rules import (
|
||||
TransactionRuleResource,
|
||||
TransactionRuleActionResource,
|
||||
UpdateOrCreateTransactionRuleResource,
|
||||
)
|
||||
from apps.export_app.resources.dca import (
|
||||
DCAStrategyResource,
|
||||
DCAEntryResource,
|
||||
)
|
||||
from apps.export_app.resources.import_app import (
|
||||
ImportProfileResource,
|
||||
)
|
||||
from apps.common.decorators.htmx import only_htmx
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def export_index(request):
|
||||
return render(request, "export_app/pages/index.html")
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET", "POST"])
|
||||
def export_form(request):
|
||||
timestamp = timezone.localtime(timezone.now()).strftime("%Y-%m-%dT%H-%M-%S")
|
||||
|
||||
if request.method == "POST":
|
||||
form = ExportForm(request.POST)
|
||||
if form.is_valid():
|
||||
zip_buffer = BytesIO()
|
||||
|
||||
export_accounts = form.cleaned_data.get("accounts", False)
|
||||
export_currencies = form.cleaned_data.get("currencies", False)
|
||||
export_transactions = form.cleaned_data.get("transactions", False)
|
||||
export_categories = form.cleaned_data.get("categories", False)
|
||||
export_tags = form.cleaned_data.get("tags", False)
|
||||
export_entities = form.cleaned_data.get("entities", False)
|
||||
export_installment_plans = form.cleaned_data.get("installment_plans", False)
|
||||
export_recurring_transactions = form.cleaned_data.get(
|
||||
"recurring_transactions", False
|
||||
)
|
||||
|
||||
export_exchange_rates_services = form.cleaned_data.get(
|
||||
"exchange_rates_services", False
|
||||
)
|
||||
export_exchange_rates = form.cleaned_data.get("exchange_rates", False)
|
||||
export_rules = form.cleaned_data.get("rules", False)
|
||||
export_dca = form.cleaned_data.get("dca", False)
|
||||
export_import_profiles = form.cleaned_data.get("import_profiles", False)
|
||||
|
||||
exports = []
|
||||
if export_accounts:
|
||||
exports.append((AccountResource().export(), "accounts"))
|
||||
if export_currencies:
|
||||
exports.append((CurrencyResource().export(), "currencies"))
|
||||
if export_transactions:
|
||||
exports.append((TransactionResource().export(), "transactions"))
|
||||
if export_categories:
|
||||
exports.append(
|
||||
(TransactionCategoyResource().export(), "transactions_categories")
|
||||
)
|
||||
if export_tags:
|
||||
exports.append((TransactionTagResource().export(), "transactions_tags"))
|
||||
if export_entities:
|
||||
exports.append(
|
||||
(TransactionEntityResource().export(), "transactions_entities")
|
||||
)
|
||||
if export_installment_plans:
|
||||
exports.append(
|
||||
(InstallmentPlanResource().export(), "installment_plans")
|
||||
)
|
||||
if export_recurring_transactions:
|
||||
exports.append(
|
||||
(RecurringTransactionResource().export(), "recurring_transactions")
|
||||
)
|
||||
if export_exchange_rates_services:
|
||||
exports.append(
|
||||
(ExchangeRateServiceResource().export(), "automatic_exchange_rates")
|
||||
)
|
||||
if export_exchange_rates:
|
||||
exports.append((ExchangeRateResource().export(), "exchange_rates"))
|
||||
if export_rules:
|
||||
exports.append(
|
||||
(TransactionRuleResource().export(), "transaction_rules")
|
||||
)
|
||||
exports.append(
|
||||
(
|
||||
TransactionRuleActionResource().export(),
|
||||
"transaction_rules_actions",
|
||||
)
|
||||
)
|
||||
exports.append(
|
||||
(
|
||||
UpdateOrCreateTransactionRuleResource().export(),
|
||||
"transaction_rules_update_or_create",
|
||||
)
|
||||
)
|
||||
if export_dca:
|
||||
exports.append((DCAStrategyResource().export(), "dca_strategies"))
|
||||
exports.append(
|
||||
(
|
||||
DCAEntryResource().export(),
|
||||
"dca_entries",
|
||||
)
|
||||
)
|
||||
if export_import_profiles:
|
||||
exports.append((ImportProfileResource().export(), "import_profiles"))
|
||||
|
||||
if len(exports) >= 2:
|
||||
with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zip_file:
|
||||
for dataset, name in exports:
|
||||
zip_file.writestr(f"{name}.csv", dataset.csv)
|
||||
|
||||
response = HttpResponse(
|
||||
zip_buffer.getvalue(),
|
||||
content_type="application/zip",
|
||||
headers={
|
||||
"HX-Trigger": "hide_offcanvas, updated",
|
||||
"Content-Disposition": f'attachment; filename="{timestamp}_WYGIWYH_export.zip"',
|
||||
},
|
||||
)
|
||||
return response
|
||||
elif len(exports) == 1:
|
||||
dataset, name = exports[0]
|
||||
|
||||
response = HttpResponse(
|
||||
dataset.csv,
|
||||
content_type="text/csv",
|
||||
headers={
|
||||
"HX-Trigger": "hide_offcanvas, updated",
|
||||
"Content-Disposition": f'attachment; filename="{timestamp}_WYGIWYH_export_{name}.csv"',
|
||||
},
|
||||
)
|
||||
return response
|
||||
else:
|
||||
return HttpResponse(
|
||||
_("You have to select at least one export"),
|
||||
)
|
||||
|
||||
else:
|
||||
form = ExportForm()
|
||||
|
||||
return render(request, "export_app/fragments/export.html", context={"form": form})
|
||||
|
||||
|
||||
@only_htmx
|
||||
@login_required
|
||||
@require_http_methods(["GET", "POST"])
|
||||
def import_form(request):
|
||||
if request.method == "POST":
|
||||
form = RestoreForm(request.POST, request.FILES)
|
||||
if form.is_valid():
|
||||
try:
|
||||
process_imports(request, form.cleaned_data)
|
||||
messages.success(request, _("Data restored successfully"))
|
||||
return HttpResponse(
|
||||
status=204,
|
||||
headers={
|
||||
"HX-Trigger": "hide_offcanvas, updated",
|
||||
},
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error("Error importing", exc_info=e)
|
||||
messages.error(
|
||||
request,
|
||||
_(
|
||||
"There was an error restoring your data. Check the logs for more details."
|
||||
),
|
||||
)
|
||||
else:
|
||||
form = RestoreForm()
|
||||
|
||||
response = render(request, "export_app/fragments/restore.html", {"form": form})
|
||||
response["HX-Trigger"] = "updated"
|
||||
return response
|
||||
|
||||
|
||||
def process_imports(request, cleaned_data):
|
||||
# Define import order to handle dependencies
|
||||
import_order = [
|
||||
("currencies", CurrencyResource),
|
||||
(
|
||||
"currencies",
|
||||
CurrencyResource,
|
||||
), # We do a double pass because exchange_currency may not exist when currency is initially created
|
||||
("accounts", AccountResource),
|
||||
("transactions_categories", TransactionCategoyResource),
|
||||
("transactions_tags", TransactionTagResource),
|
||||
("transactions_entities", TransactionEntityResource),
|
||||
("automatic_exchange_rates", ExchangeRateServiceResource),
|
||||
("exchange_rates", ExchangeRateResource),
|
||||
("installment_plans", InstallmentPlanResource),
|
||||
("recurring_transactions", RecurringTransactionResource),
|
||||
("transactions", TransactionResource),
|
||||
("dca_strategies", DCAStrategyResource),
|
||||
("dca_entries", DCAEntryResource),
|
||||
("import_profiles", ImportProfileResource),
|
||||
("transaction_rules", TransactionRuleResource),
|
||||
("transaction_rules_actions", TransactionRuleActionResource),
|
||||
("transaction_rules_update_or_create", UpdateOrCreateTransactionRuleResource),
|
||||
]
|
||||
|
||||
def import_dataset(content, resource_class, field_name):
|
||||
try:
|
||||
# Create a new resource instance
|
||||
resource = resource_class()
|
||||
|
||||
# Create dataset from CSV content
|
||||
dataset = Dataset()
|
||||
dataset.load(content, format="csv")
|
||||
|
||||
# Perform the import
|
||||
result = resource.import_data(
|
||||
dataset,
|
||||
dry_run=False,
|
||||
raise_errors=True,
|
||||
collect_failed_rows=True,
|
||||
use_transactions=False,
|
||||
skip_unchanged=True,
|
||||
)
|
||||
|
||||
if result.has_errors():
|
||||
raise ImportError(f"Failed rows: {result.failed_dataset}")
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error importing {field_name}: {str(e)}")
|
||||
raise ImportError(f"Error importing {field_name}: {str(e)}")
|
||||
|
||||
with transaction.atomic():
|
||||
files = {}
|
||||
|
||||
if zip_file := cleaned_data.get("zip_file"):
|
||||
# Process ZIP file
|
||||
with zipfile.ZipFile(zip_file) as z:
|
||||
for filename in z.namelist():
|
||||
name = filename.replace(".csv", "")
|
||||
with z.open(filename) as f:
|
||||
content = f.read().decode("utf-8")
|
||||
|
||||
files[name] = content
|
||||
|
||||
for field_name, resource_class in import_order:
|
||||
if field_name in files.keys():
|
||||
content = files[field_name]
|
||||
import_dataset(content, resource_class, field_name)
|
||||
else:
|
||||
# Process individual files
|
||||
for field_name, resource_class in import_order:
|
||||
if csv_file := cleaned_data.get(field_name):
|
||||
content = csv_file.read().decode("utf-8")
|
||||
import_dataset(content, resource_class, field_name)
|
||||
0
app/apps/export_app/widgets/__init__.py
Normal file
0
app/apps/export_app/widgets/__init__.py
Normal file
22
app/apps/export_app/widgets/foreign_key.py
Normal file
22
app/apps/export_app/widgets/foreign_key.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from import_export.widgets import ForeignKeyWidget
|
||||
|
||||
|
||||
class AutoCreateForeignKeyWidget(ForeignKeyWidget):
|
||||
def clean(self, value, row=None, *args, **kwargs):
|
||||
if value:
|
||||
try:
|
||||
return super().clean(value, row, **kwargs)
|
||||
except self.model.DoesNotExist:
|
||||
return self.model.objects.create(name=value)
|
||||
return None
|
||||
|
||||
|
||||
class SkipMissingForeignKeyWidget(ForeignKeyWidget):
|
||||
def clean(self, value, row=None, *args, **kwargs):
|
||||
if not value:
|
||||
return None
|
||||
|
||||
try:
|
||||
return super().clean(value, row, *args, **kwargs)
|
||||
except self.model.DoesNotExist:
|
||||
return None
|
||||
21
app/apps/export_app/widgets/many_to_many.py
Normal file
21
app/apps/export_app/widgets/many_to_many.py
Normal 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
|
||||
18
app/apps/export_app/widgets/numbers.py
Normal file
18
app/apps/export_app/widgets/numbers.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from decimal import Decimal
|
||||
|
||||
from import_export.widgets import NumberWidget
|
||||
|
||||
|
||||
class UniversalDecimalWidget(NumberWidget):
|
||||
def clean(self, value, row=None, *args, **kwargs):
|
||||
if self.is_empty(value):
|
||||
return None
|
||||
# Replace comma with dot if present
|
||||
if isinstance(value, str):
|
||||
value = value.replace(",", ".")
|
||||
return Decimal(str(value))
|
||||
|
||||
def render(self, value, obj=None, **kwargs):
|
||||
if value is None:
|
||||
return ""
|
||||
return str(value).replace(",", ".")
|
||||
7
app/apps/export_app/widgets/string.py
Normal file
7
app/apps/export_app/widgets/string.py
Normal 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
|
||||
@@ -9,11 +9,12 @@ from apps.common.widgets.datepicker import (
|
||||
AirDatePickerInput,
|
||||
)
|
||||
from apps.transactions.models import TransactionCategory
|
||||
from apps.common.widgets.tom_select import TomSelect
|
||||
|
||||
|
||||
class SingleMonthForm(forms.Form):
|
||||
month = forms.DateField(
|
||||
widget=AirMonthYearPickerInput(clear_button=False), label="", required=False
|
||||
widget=AirMonthYearPickerInput(clear_button=False), label="", required=True
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -28,7 +29,7 @@ class SingleMonthForm(forms.Form):
|
||||
|
||||
class SingleYearForm(forms.Form):
|
||||
year = forms.DateField(
|
||||
widget=AirYearPickerInput(clear_button=False), label="", required=False
|
||||
widget=AirYearPickerInput(clear_button=False), label="", required=True
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -43,10 +44,10 @@ class SingleYearForm(forms.Form):
|
||||
|
||||
class MonthRangeForm(forms.Form):
|
||||
month_from = forms.DateField(
|
||||
widget=AirMonthYearPickerInput(clear_button=False), label="", required=False
|
||||
widget=AirMonthYearPickerInput(clear_button=False), label="", required=True
|
||||
)
|
||||
month_to = forms.DateField(
|
||||
widget=AirMonthYearPickerInput(clear_button=False), label="", required=False
|
||||
widget=AirMonthYearPickerInput(clear_button=False), label="", required=True
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -66,10 +67,10 @@ class MonthRangeForm(forms.Form):
|
||||
|
||||
class YearRangeForm(forms.Form):
|
||||
year_from = forms.DateField(
|
||||
widget=AirYearPickerInput(clear_button=False), label="", required=False
|
||||
widget=AirYearPickerInput(clear_button=False), label="", required=True
|
||||
)
|
||||
year_to = forms.DateField(
|
||||
widget=AirYearPickerInput(clear_button=False), label="", required=False
|
||||
widget=AirYearPickerInput(clear_button=False), label="", required=True
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -89,10 +90,10 @@ class YearRangeForm(forms.Form):
|
||||
|
||||
class DateRangeForm(forms.Form):
|
||||
date_from = forms.DateField(
|
||||
widget=AirDatePickerInput(clear_button=False), label="", required=False
|
||||
widget=AirDatePickerInput(clear_button=False), label="", required=True
|
||||
)
|
||||
date_to = forms.DateField(
|
||||
widget=AirDatePickerInput(clear_button=False), label="", required=False
|
||||
widget=AirDatePickerInput(clear_button=False), label="", required=True
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -115,7 +116,9 @@ class CategoryForm(forms.Form):
|
||||
category = forms.ModelChoiceField(
|
||||
required=False,
|
||||
label=_("Category"),
|
||||
empty_label=_("Uncategorized"),
|
||||
queryset=TransactionCategory.objects.filter(active=True),
|
||||
widget=TomSelect(clear_button=True),
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
@@ -29,4 +29,14 @@ urlpatterns = [
|
||||
views.category_sum_by_currency,
|
||||
name="category_sum_by_currency",
|
||||
),
|
||||
path(
|
||||
"insights/late-transactions/",
|
||||
views.late_transactions,
|
||||
name="insights_late_transactions",
|
||||
),
|
||||
path(
|
||||
"insights/latest-transactions/",
|
||||
views.latest_transactions,
|
||||
name="insights_latest_transactions",
|
||||
),
|
||||
]
|
||||
|
||||
@@ -3,7 +3,7 @@ from django.db.models.functions import Coalesce
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
def get_category_sums_by_account(queryset, category):
|
||||
def get_category_sums_by_account(queryset, category=None):
|
||||
"""
|
||||
Returns income/expense sums per account for a specific category.
|
||||
"""
|
||||
@@ -11,10 +11,10 @@ def get_category_sums_by_account(queryset, category):
|
||||
queryset.filter(category=category)
|
||||
.values("account__name")
|
||||
.annotate(
|
||||
income=Coalesce(
|
||||
current_income=Coalesce(
|
||||
Sum(
|
||||
Case(
|
||||
When(type="IN", then="amount"),
|
||||
When(type="IN", is_paid=True, then="amount"),
|
||||
default=Value(0),
|
||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||
)
|
||||
@@ -22,10 +22,32 @@ def get_category_sums_by_account(queryset, category):
|
||||
Value(0),
|
||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||
),
|
||||
expense=Coalesce(
|
||||
current_expense=Coalesce(
|
||||
Sum(
|
||||
Case(
|
||||
When(type="EX", then=-F("amount")),
|
||||
When(type="EX", is_paid=True, then=-F("amount")),
|
||||
default=Value(0),
|
||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||
)
|
||||
),
|
||||
Value(0),
|
||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||
),
|
||||
projected_income=Coalesce(
|
||||
Sum(
|
||||
Case(
|
||||
When(type="IN", is_paid=False, then="amount"),
|
||||
default=Value(0),
|
||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||
)
|
||||
),
|
||||
Value(0),
|
||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||
),
|
||||
projected_expense=Coalesce(
|
||||
Sum(
|
||||
Case(
|
||||
When(type="EX", is_paid=False, then=-F("amount")),
|
||||
default=Value(0),
|
||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||
)
|
||||
@@ -41,29 +63,37 @@ def get_category_sums_by_account(queryset, category):
|
||||
"labels": [item["account__name"] for item in sums],
|
||||
"datasets": [
|
||||
{
|
||||
"label": _("Income"),
|
||||
"data": [float(item["income"]) for item in sums],
|
||||
"label": _("Current Income"),
|
||||
"data": [float(item["current_income"]) for item in sums],
|
||||
},
|
||||
{
|
||||
"label": _("Expenses"),
|
||||
"data": [float(item["expense"]) for item in sums],
|
||||
"label": _("Current Expenses"),
|
||||
"data": [float(item["current_expense"]) for item in sums],
|
||||
},
|
||||
{
|
||||
"label": _("Projected Income"),
|
||||
"data": [float(item["projected_income"]) for item in sums],
|
||||
},
|
||||
{
|
||||
"label": _("Projected Expenses"),
|
||||
"data": [float(item["projected_expense"]) for item in sums],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def get_category_sums_by_currency(queryset, category):
|
||||
def get_category_sums_by_currency(queryset, category=None):
|
||||
"""
|
||||
Returns income/expense sums per currency for a specific category.
|
||||
"""
|
||||
sums = (
|
||||
queryset.filter(category=category)
|
||||
.values("account__currency__code")
|
||||
.values("account__currency__name")
|
||||
.annotate(
|
||||
income=Coalesce(
|
||||
current_income=Coalesce(
|
||||
Sum(
|
||||
Case(
|
||||
When(type="IN", then="amount"),
|
||||
When(type="IN", is_paid=True, then="amount"),
|
||||
default=Value(0),
|
||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||
)
|
||||
@@ -71,10 +101,32 @@ def get_category_sums_by_currency(queryset, category):
|
||||
Value(0),
|
||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||
),
|
||||
expense=Coalesce(
|
||||
current_expense=Coalesce(
|
||||
Sum(
|
||||
Case(
|
||||
When(type="EX", then=-F("amount")),
|
||||
When(type="EX", is_paid=True, then=-F("amount")),
|
||||
default=Value(0),
|
||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||
)
|
||||
),
|
||||
Value(0),
|
||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||
),
|
||||
projected_income=Coalesce(
|
||||
Sum(
|
||||
Case(
|
||||
When(type="IN", is_paid=False, then="amount"),
|
||||
default=Value(0),
|
||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||
)
|
||||
),
|
||||
Value(0),
|
||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||
),
|
||||
projected_expense=Coalesce(
|
||||
Sum(
|
||||
Case(
|
||||
When(type="EX", is_paid=False, then=-F("amount")),
|
||||
default=Value(0),
|
||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||
)
|
||||
@@ -83,19 +135,27 @@ def get_category_sums_by_currency(queryset, category):
|
||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||
),
|
||||
)
|
||||
.order_by("account__currency__code")
|
||||
.order_by("account__currency__name")
|
||||
)
|
||||
|
||||
return {
|
||||
"labels": [item["account__currency__code"] for item in sums],
|
||||
"labels": [item["account__currency__name"] for item in sums],
|
||||
"datasets": [
|
||||
{
|
||||
"label": _("Income"),
|
||||
"data": [float(item["income"]) for item in sums],
|
||||
"label": _("Current Income"),
|
||||
"data": [float(item["current_income"]) for item in sums],
|
||||
},
|
||||
{
|
||||
"label": _("Expenses"),
|
||||
"data": [float(item["expense"]) for item in sums],
|
||||
"label": _("Current Expenses"),
|
||||
"data": [float(item["current_expense"]) for item in sums],
|
||||
},
|
||||
{
|
||||
"label": _("Projected Income"),
|
||||
"data": [float(item["projected_income"]) for item in sums],
|
||||
},
|
||||
{
|
||||
"label": _("Projected Expenses"),
|
||||
"data": [float(item["projected_expense"]) for item in sums],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ from apps.insights.utils.sankey import (
|
||||
generate_sankey_data_by_currency,
|
||||
)
|
||||
from apps.insights.utils.transactions import get_transactions
|
||||
from apps.transactions.models import TransactionCategory
|
||||
from apps.transactions.models import TransactionCategory, Transaction
|
||||
|
||||
|
||||
@login_required
|
||||
@@ -116,7 +116,7 @@ def category_explorer_index(request):
|
||||
@require_http_methods(["GET"])
|
||||
def category_sum_by_account(request):
|
||||
# Get filtered transactions
|
||||
transactions = get_transactions(request)
|
||||
transactions = get_transactions(request, include_silent=True)
|
||||
|
||||
category = request.GET.get("category")
|
||||
|
||||
@@ -126,7 +126,7 @@ def category_sum_by_account(request):
|
||||
# Generate data
|
||||
account_data = get_category_sums_by_account(transactions, category)
|
||||
else:
|
||||
account_data = None
|
||||
account_data = get_category_sums_by_account(transactions, category=None)
|
||||
|
||||
return render(
|
||||
request,
|
||||
@@ -140,7 +140,7 @@ def category_sum_by_account(request):
|
||||
@require_http_methods(["GET"])
|
||||
def category_sum_by_currency(request):
|
||||
# Get filtered transactions
|
||||
transactions = get_transactions(request)
|
||||
transactions = get_transactions(request, include_silent=True)
|
||||
|
||||
category = request.GET.get("category")
|
||||
|
||||
@@ -150,12 +150,40 @@ def category_sum_by_currency(request):
|
||||
# Generate data
|
||||
currency_data = get_category_sums_by_currency(transactions, category)
|
||||
else:
|
||||
currency_data = None
|
||||
|
||||
print(currency_data)
|
||||
currency_data = get_category_sums_by_currency(transactions, category=None)
|
||||
|
||||
return render(
|
||||
request,
|
||||
"insights/fragments/category_explorer/charts/currency.html",
|
||||
{"currency_data": currency_data},
|
||||
)
|
||||
|
||||
|
||||
@only_htmx
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def latest_transactions(request):
|
||||
limit = timezone.now() - relativedelta(days=3)
|
||||
transactions = Transaction.objects.filter(created_at__gte=limit).order_by("-id")[
|
||||
:30
|
||||
]
|
||||
|
||||
return render(
|
||||
request,
|
||||
"insights/fragments/latest_transactions.html",
|
||||
{"transactions": transactions},
|
||||
)
|
||||
|
||||
|
||||
@only_htmx
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def late_transactions(request):
|
||||
now = timezone.localdate(timezone.now())
|
||||
transactions = Transaction.objects.filter(is_paid=False, date__lt=now)
|
||||
|
||||
return render(
|
||||
request,
|
||||
"insights/fragments/late_transactions.html",
|
||||
{"transactions": transactions},
|
||||
)
|
||||
|
||||
0
app/apps/mini_tools/utils/__init__.py
Normal file
0
app/apps/mini_tools/utils/__init__.py
Normal file
85
app/apps/mini_tools/utils/exchange_rate_map.py
Normal file
85
app/apps/mini_tools/utils/exchange_rate_map.py
Normal file
@@ -0,0 +1,85 @@
|
||||
from typing import Dict
|
||||
|
||||
from django.db.models import Func, F, Value
|
||||
from django.db.models.functions import Extract
|
||||
from django.utils import timezone
|
||||
|
||||
from apps.currencies.models import ExchangeRate
|
||||
|
||||
|
||||
def get_currency_exchange_map(date=None) -> Dict[str, dict]:
|
||||
"""
|
||||
Creates a nested dictionary of exchange rates and currency information.
|
||||
|
||||
Returns:
|
||||
{
|
||||
'BTC': {
|
||||
'decimal_places': 8,
|
||||
'prefix': '₿',
|
||||
'suffix': '',
|
||||
'rates': {'USD': Decimal('34000.00'), 'EUR': Decimal('31000.00')}
|
||||
},
|
||||
'USD': {
|
||||
'decimal_places': 2,
|
||||
'prefix': '$',
|
||||
'suffix': '',
|
||||
'rates': {'BTC': Decimal('0.0000294'), 'EUR': Decimal('0.91')}
|
||||
},
|
||||
...
|
||||
}
|
||||
"""
|
||||
if date is None:
|
||||
date = timezone.localtime(timezone.now())
|
||||
|
||||
# Get all exchange rates for the closest date
|
||||
exchange_rates = (
|
||||
ExchangeRate.objects.select_related(
|
||||
"from_currency", "to_currency"
|
||||
) # Optimize currency queries
|
||||
.annotate(
|
||||
date_diff=Func(Extract(F("date") - Value(date), "epoch"), function="ABS"),
|
||||
effective_rate=F("rate"),
|
||||
)
|
||||
.order_by("from_currency", "to_currency", "date_diff")
|
||||
.distinct("from_currency", "to_currency")
|
||||
)
|
||||
|
||||
# Initialize the result dictionary
|
||||
rate_map = {}
|
||||
|
||||
# Build the exchange rate mapping with currency info
|
||||
for rate in exchange_rates:
|
||||
# Add from_currency info if not exists
|
||||
if rate.from_currency.name not in rate_map:
|
||||
rate_map[rate.from_currency.name] = {
|
||||
"decimal_places": rate.from_currency.decimal_places,
|
||||
"prefix": rate.from_currency.prefix,
|
||||
"suffix": rate.from_currency.suffix,
|
||||
"rates": {},
|
||||
}
|
||||
|
||||
# Add to_currency info if not exists
|
||||
if rate.to_currency.name not in rate_map:
|
||||
rate_map[rate.to_currency.name] = {
|
||||
"decimal_places": rate.to_currency.decimal_places,
|
||||
"prefix": rate.to_currency.prefix,
|
||||
"suffix": rate.to_currency.suffix,
|
||||
"rates": {},
|
||||
}
|
||||
|
||||
# Add direct rate
|
||||
rate_map[rate.from_currency.name]["rates"][rate.to_currency.name] = {
|
||||
"rate": rate.rate,
|
||||
"decimal_places": rate.to_currency.decimal_places,
|
||||
"prefix": rate.to_currency.prefix,
|
||||
"suffix": rate.to_currency.suffix,
|
||||
}
|
||||
# Add inverse rate
|
||||
rate_map[rate.to_currency.name]["rates"][rate.from_currency.name] = {
|
||||
"rate": 1 / rate.rate,
|
||||
"decimal_places": rate.from_currency.decimal_places,
|
||||
"prefix": rate.from_currency.prefix,
|
||||
"suffix": rate.from_currency.suffix,
|
||||
}
|
||||
|
||||
return rate_map
|
||||
@@ -5,6 +5,7 @@ from apps.common.widgets.decimal import convert_to_decimal
|
||||
from apps.currencies.models import Currency
|
||||
from apps.currencies.utils.convert import convert
|
||||
from apps.mini_tools.forms import CurrencyConverterForm
|
||||
from apps.mini_tools.utils.exchange_rate_map import get_currency_exchange_map
|
||||
|
||||
|
||||
@login_required
|
||||
@@ -14,11 +15,13 @@ def unit_price_calculator(request):
|
||||
|
||||
@login_required
|
||||
def currency_converter(request):
|
||||
rate_map = get_currency_exchange_map()
|
||||
|
||||
form = CurrencyConverterForm()
|
||||
return render(
|
||||
request,
|
||||
"mini_tools/currency_converter/currency_converter.html",
|
||||
context={"form": form},
|
||||
context={"form": form, "rate_map": rate_map},
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.1.6 on 2025-02-24 19:32
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0018_alter_usersettings_start_page'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='usersettings',
|
||||
name='language',
|
||||
field=models.CharField(choices=[('auto', 'Auto'), ('de', 'Deutsch'), ('en', 'English'), ('nl', 'Nederlands'), ('pt-br', 'Português (Brasil)')], default='auto', max_length=10, verbose_name='Language'),
|
||||
),
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,8 +8,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-02-16 13:04-0300\n"
|
||||
"PO-Revision-Date: 2025-02-12 06:58+0100\n"
|
||||
"POT-Creation-Date: 2025-02-24 16:30-0300\n"
|
||||
"PO-Revision-Date: 2025-02-22 15:03+0100\n"
|
||||
"Last-Translator: Dimitri Decrock <dimitri@fam-decrock.eu>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: nl\n"
|
||||
@@ -27,10 +27,10 @@ msgstr "Groepsnaam"
|
||||
#: apps/currencies/forms.py:53 apps/currencies/forms.py:91
|
||||
#: apps/currencies/forms.py:142 apps/dca/forms.py:49 apps/dca/forms.py:224
|
||||
#: apps/import_app/forms.py:34 apps/rules/forms.py:45 apps/rules/forms.py:87
|
||||
#: apps/rules/forms.py:359 apps/transactions/forms.py:190
|
||||
#: apps/transactions/forms.py:257 apps/transactions/forms.py:581
|
||||
#: apps/transactions/forms.py:624 apps/transactions/forms.py:656
|
||||
#: apps/transactions/forms.py:691 apps/transactions/forms.py:827
|
||||
#: apps/rules/forms.py:359 apps/transactions/forms.py:191
|
||||
#: apps/transactions/forms.py:258 apps/transactions/forms.py:582
|
||||
#: apps/transactions/forms.py:625 apps/transactions/forms.py:657
|
||||
#: apps/transactions/forms.py:692 apps/transactions/forms.py:828
|
||||
msgid "Update"
|
||||
msgstr "Bijwerken"
|
||||
|
||||
@@ -39,10 +39,10 @@ msgstr "Bijwerken"
|
||||
#: apps/currencies/forms.py:99 apps/currencies/forms.py:150
|
||||
#: apps/dca/forms.py:57 apps/dca/forms.py:232 apps/import_app/forms.py:42
|
||||
#: apps/rules/forms.py:53 apps/rules/forms.py:95 apps/rules/forms.py:367
|
||||
#: apps/transactions/forms.py:174 apps/transactions/forms.py:199
|
||||
#: apps/transactions/forms.py:589 apps/transactions/forms.py:632
|
||||
#: apps/transactions/forms.py:664 apps/transactions/forms.py:699
|
||||
#: apps/transactions/forms.py:835
|
||||
#: apps/transactions/forms.py:176 apps/transactions/forms.py:200
|
||||
#: apps/transactions/forms.py:590 apps/transactions/forms.py:633
|
||||
#: apps/transactions/forms.py:665 apps/transactions/forms.py:700
|
||||
#: apps/transactions/forms.py:836
|
||||
#: templates/account_groups/fragments/list.html:9
|
||||
#: templates/accounts/fragments/list.html:9
|
||||
#: templates/categories/fragments/list.html:9
|
||||
@@ -70,21 +70,22 @@ msgid "New balance"
|
||||
msgstr "Nieuw saldo"
|
||||
|
||||
#: apps/accounts/forms.py:119 apps/dca/forms.py:85 apps/dca/forms.py:92
|
||||
#: apps/insights/forms.py:117 apps/rules/forms.py:168 apps/rules/forms.py:183
|
||||
#: apps/insights/forms.py:118 apps/rules/forms.py:168 apps/rules/forms.py:183
|
||||
#: apps/rules/models.py:32 apps/rules/models.py:280
|
||||
#: apps/transactions/forms.py:39 apps/transactions/forms.py:291
|
||||
#: apps/transactions/forms.py:298 apps/transactions/forms.py:478
|
||||
#: apps/transactions/forms.py:723 apps/transactions/models.py:203
|
||||
#: apps/transactions/forms.py:39 apps/transactions/forms.py:292
|
||||
#: apps/transactions/forms.py:299 apps/transactions/forms.py:479
|
||||
#: apps/transactions/forms.py:724 apps/transactions/models.py:203
|
||||
#: apps/transactions/models.py:378 apps/transactions/models.py:558
|
||||
msgid "Category"
|
||||
msgstr "Categorie"
|
||||
|
||||
#: apps/accounts/forms.py:126 apps/dca/forms.py:101 apps/dca/forms.py:109
|
||||
#: apps/export_app/forms.py:38 apps/export_app/forms.py:127
|
||||
#: apps/rules/forms.py:171 apps/rules/forms.py:180 apps/rules/models.py:33
|
||||
#: apps/rules/models.py:284 apps/transactions/filters.py:74
|
||||
#: apps/transactions/forms.py:47 apps/transactions/forms.py:307
|
||||
#: apps/transactions/forms.py:315 apps/transactions/forms.py:471
|
||||
#: apps/transactions/forms.py:716 apps/transactions/models.py:209
|
||||
#: apps/transactions/forms.py:47 apps/transactions/forms.py:308
|
||||
#: apps/transactions/forms.py:316 apps/transactions/forms.py:472
|
||||
#: apps/transactions/forms.py:717 apps/transactions/models.py:209
|
||||
#: apps/transactions/models.py:380 apps/transactions/models.py:562
|
||||
#: templates/includes/navbar.html:108 templates/tags/fragments/list.html:5
|
||||
#: templates/tags/pages/index.html:4
|
||||
@@ -159,13 +160,14 @@ msgstr ""
|
||||
|
||||
#: apps/accounts/models.py:59 apps/rules/forms.py:160 apps/rules/forms.py:173
|
||||
#: apps/rules/models.py:24 apps/rules/models.py:236
|
||||
#: apps/transactions/forms.py:59 apps/transactions/forms.py:463
|
||||
#: apps/transactions/forms.py:708 apps/transactions/models.py:176
|
||||
#: apps/transactions/forms.py:59 apps/transactions/forms.py:464
|
||||
#: apps/transactions/forms.py:709 apps/transactions/models.py:176
|
||||
#: apps/transactions/models.py:338 apps/transactions/models.py:540
|
||||
msgid "Account"
|
||||
msgstr "Rekening"
|
||||
|
||||
#: apps/accounts/models.py:60 apps/transactions/filters.py:53
|
||||
#: apps/accounts/models.py:60 apps/export_app/forms.py:14
|
||||
#: apps/export_app/forms.py:124 apps/transactions/filters.py:53
|
||||
#: templates/accounts/fragments/list.html:5
|
||||
#: templates/accounts/pages/index.html:4 templates/includes/navbar.html:114
|
||||
#: templates/includes/navbar.html:116
|
||||
@@ -337,12 +339,12 @@ msgstr "Info"
|
||||
msgid "Cache cleared successfully"
|
||||
msgstr "Categorie succesvol bijgewerkt"
|
||||
|
||||
#: apps/common/widgets/datepicker.py:47 apps/common/widgets/datepicker.py:186
|
||||
#: apps/common/widgets/datepicker.py:244
|
||||
#: apps/common/widgets/datepicker.py:53 apps/common/widgets/datepicker.py:206
|
||||
#: apps/common/widgets/datepicker.py:264
|
||||
msgid "Today"
|
||||
msgstr "Vandaag"
|
||||
|
||||
#: apps/common/widgets/datepicker.py:123
|
||||
#: apps/common/widgets/datepicker.py:139
|
||||
msgid "Now"
|
||||
msgstr "Nu"
|
||||
|
||||
@@ -371,7 +373,7 @@ msgstr "Achtervoegsel"
|
||||
|
||||
#: apps/currencies/forms.py:69 apps/dca/models.py:156 apps/rules/forms.py:163
|
||||
#: apps/rules/forms.py:176 apps/rules/models.py:27 apps/rules/models.py:248
|
||||
#: apps/transactions/forms.py:63 apps/transactions/forms.py:319
|
||||
#: apps/transactions/forms.py:63 apps/transactions/forms.py:320
|
||||
#: apps/transactions/models.py:186
|
||||
#: templates/dca/fragments/strategy/details.html:52
|
||||
#: templates/exchange_rates/fragments/table.html:10
|
||||
@@ -391,7 +393,8 @@ msgstr "Munteenheids Naam"
|
||||
msgid "Decimal Places"
|
||||
msgstr "Cijfers na de komma"
|
||||
|
||||
#: apps/currencies/models.py:40 apps/transactions/filters.py:60
|
||||
#: apps/currencies/models.py:40 apps/export_app/forms.py:20
|
||||
#: apps/export_app/forms.py:125 apps/transactions/filters.py:60
|
||||
#: templates/currencies/fragments/list.html:5
|
||||
#: templates/currencies/pages/index.html:4 templates/includes/navbar.html:122
|
||||
#: templates/includes/navbar.html:124
|
||||
@@ -421,7 +424,8 @@ msgstr "Wisselkoers"
|
||||
msgid "Date and Time"
|
||||
msgstr "Datum en Tijd"
|
||||
|
||||
#: apps/currencies/models.py:74 templates/exchange_rates/fragments/list.html:6
|
||||
#: apps/currencies/models.py:74 apps/export_app/forms.py:62
|
||||
#: apps/export_app/forms.py:137 templates/exchange_rates/fragments/list.html:6
|
||||
#: templates/exchange_rates/pages/index.html:4
|
||||
#: templates/includes/navbar.html:126
|
||||
msgid "Exchange Rates"
|
||||
@@ -578,16 +582,14 @@ msgid "Services queued successfully"
|
||||
msgstr "Diensten succesvol in de wachtrij geplaatst"
|
||||
|
||||
#: apps/dca/forms.py:65 apps/dca/forms.py:164
|
||||
#, fuzzy
|
||||
#| msgid "Deleted transactions"
|
||||
msgid "Create transaction"
|
||||
msgstr "Verwijderde verrichtingen"
|
||||
msgstr "Maak verrichtingen"
|
||||
|
||||
#: apps/dca/forms.py:70 apps/transactions/forms.py:266
|
||||
#: apps/dca/forms.py:70 apps/transactions/forms.py:267
|
||||
msgid "From Account"
|
||||
msgstr "Van rekening"
|
||||
|
||||
#: apps/dca/forms.py:76 apps/transactions/forms.py:271
|
||||
#: apps/dca/forms.py:76 apps/transactions/forms.py:272
|
||||
msgid "To Account"
|
||||
msgstr "Naar rekening"
|
||||
|
||||
@@ -598,31 +600,29 @@ msgstr "Uitgave Transactie"
|
||||
#: apps/dca/forms.py:120 apps/dca/forms.py:130
|
||||
msgid "Type to search for a transaction to link to this entry"
|
||||
msgstr ""
|
||||
"Type om een transactie te zoeken die aan dit item moet worden gekoppeld"
|
||||
|
||||
#: apps/dca/forms.py:126 apps/dca/models.py:177
|
||||
msgid "Income Transaction"
|
||||
msgstr "Ontvangsten Transactie"
|
||||
|
||||
#: apps/dca/forms.py:210
|
||||
#, fuzzy
|
||||
#| msgid "Edit transaction"
|
||||
msgid "Link transaction"
|
||||
msgstr "Bewerk verrichting"
|
||||
msgstr "Koppel verrichting"
|
||||
|
||||
#: apps/dca/forms.py:279 apps/dca/forms.py:280 apps/dca/forms.py:285
|
||||
#: apps/dca/forms.py:289
|
||||
msgid "You must provide an account."
|
||||
msgstr ""
|
||||
msgstr "Je moet een account opgeven."
|
||||
|
||||
#: apps/dca/forms.py:294 apps/transactions/forms.py:413
|
||||
#: apps/dca/forms.py:294 apps/transactions/forms.py:414
|
||||
msgid "From and To accounts must be different."
|
||||
msgstr "Van en Naar rekening moeten verschillend zijn."
|
||||
|
||||
#: apps/dca/forms.py:308
|
||||
#, fuzzy, python-format
|
||||
#| msgid "DCA Strategies"
|
||||
#, python-format
|
||||
msgid "DCA for %(strategy_name)s"
|
||||
msgstr "DCA Strategieën"
|
||||
msgstr "DCA voor %(strategy_name)s"
|
||||
|
||||
#: apps/dca/models.py:17
|
||||
msgid "Target Currency"
|
||||
@@ -634,7 +634,7 @@ msgstr "Betaal Munteenheid"
|
||||
|
||||
#: apps/dca/models.py:27 apps/dca/models.py:179 apps/rules/forms.py:167
|
||||
#: apps/rules/forms.py:182 apps/rules/models.py:31 apps/rules/models.py:264
|
||||
#: apps/transactions/forms.py:333 apps/transactions/models.py:199
|
||||
#: apps/transactions/forms.py:334 apps/transactions/models.py:199
|
||||
#: apps/transactions/models.py:387 apps/transactions/models.py:568
|
||||
msgid "Notes"
|
||||
msgstr "Opmerkingen"
|
||||
@@ -643,7 +643,7 @@ msgstr "Opmerkingen"
|
||||
msgid "DCA Strategy"
|
||||
msgstr "DCA Strategie"
|
||||
|
||||
#: apps/dca/models.py:33
|
||||
#: apps/dca/models.py:33 apps/export_app/forms.py:145
|
||||
msgid "DCA Strategies"
|
||||
msgstr "DCA Strategieën"
|
||||
|
||||
@@ -663,7 +663,7 @@ msgstr "Ontvangen bedrag"
|
||||
msgid "DCA Entry"
|
||||
msgstr "DCA Instap"
|
||||
|
||||
#: apps/dca/models.py:185
|
||||
#: apps/dca/models.py:185 apps/export_app/forms.py:146
|
||||
msgid "DCA Entries"
|
||||
msgstr "DCA Idems"
|
||||
|
||||
@@ -691,6 +691,119 @@ msgstr "Item succesvol bijgewerkt"
|
||||
msgid "Entry deleted successfully"
|
||||
msgstr "Item succesvol verwijderd"
|
||||
|
||||
#: apps/export_app/forms.py:26 apps/export_app/forms.py:129
|
||||
#: apps/transactions/models.py:256 templates/includes/navbar.html:57
|
||||
#: templates/includes/navbar.html:104
|
||||
#: templates/recurring_transactions/fragments/list_transactions.html:5
|
||||
#: templates/recurring_transactions/fragments/table.html:37
|
||||
#: templates/transactions/pages/transactions.html:5
|
||||
msgid "Transactions"
|
||||
msgstr "Verrichtingen"
|
||||
|
||||
#: apps/export_app/forms.py:32 apps/export_app/forms.py:126
|
||||
#: apps/transactions/filters.py:67 templates/categories/fragments/list.html:5
|
||||
#: templates/categories/pages/index.html:4 templates/includes/navbar.html:106
|
||||
msgid "Categories"
|
||||
msgstr "Categorieën"
|
||||
|
||||
#: apps/export_app/forms.py:44 apps/export_app/forms.py:128
|
||||
#: apps/rules/forms.py:172 apps/rules/forms.py:181 apps/rules/models.py:34
|
||||
#: apps/rules/models.py:276 apps/transactions/filters.py:81
|
||||
#: apps/transactions/forms.py:55 apps/transactions/forms.py:487
|
||||
#: apps/transactions/forms.py:732 apps/transactions/models.py:161
|
||||
#: apps/transactions/models.py:214 apps/transactions/models.py:383
|
||||
#: apps/transactions/models.py:565 templates/entities/fragments/list.html:5
|
||||
#: templates/entities/pages/index.html:4 templates/includes/navbar.html:110
|
||||
msgid "Entities"
|
||||
msgstr "Bedrijven"
|
||||
|
||||
#: apps/export_app/forms.py:50 apps/export_app/forms.py:132
|
||||
#: apps/transactions/models.py:592 templates/includes/navbar.html:74
|
||||
#: templates/recurring_transactions/fragments/list.html:5
|
||||
#: templates/recurring_transactions/pages/index.html:4
|
||||
msgid "Recurring Transactions"
|
||||
msgstr "Terugkerende Verrichtingen"
|
||||
|
||||
#: apps/export_app/forms.py:56 apps/export_app/forms.py:130
|
||||
#: apps/transactions/models.py:391 templates/includes/navbar.html:72
|
||||
#: templates/installment_plans/fragments/list.html:5
|
||||
#: templates/installment_plans/pages/index.html:4
|
||||
msgid "Installment Plans"
|
||||
msgstr "Afbetalingsplannen"
|
||||
|
||||
#: apps/export_app/forms.py:68 apps/export_app/forms.py:135
|
||||
#: templates/exchange_rates_services/fragments/list.html:6
|
||||
#: templates/exchange_rates_services/pages/index.html:4
|
||||
#: templates/includes/navbar.html:138
|
||||
msgid "Automatic Exchange Rates"
|
||||
msgstr "Automatische Wisselkoersen"
|
||||
|
||||
#: apps/export_app/forms.py:74 templates/includes/navbar.html:132
|
||||
#: templates/rules/fragments/list.html:5 templates/rules/pages/index.html:4
|
||||
msgid "Rules"
|
||||
msgstr "Regels"
|
||||
|
||||
#: apps/export_app/forms.py:80 templates/cotton/transaction/item.html:56
|
||||
msgid "DCA"
|
||||
msgstr "DCA"
|
||||
|
||||
#: apps/export_app/forms.py:86 apps/export_app/forms.py:147
|
||||
#: templates/import_app/fragments/profiles/list.html:5
|
||||
#: templates/import_app/pages/profiles_index.html:4
|
||||
msgid "Import Profiles"
|
||||
msgstr "Profielen importeren"
|
||||
|
||||
#: apps/export_app/forms.py:112 templates/export_app/fragments/export.html:5
|
||||
#: templates/export_app/pages/index.html:15
|
||||
msgid "Export"
|
||||
msgstr "Exporteer"
|
||||
|
||||
#: apps/export_app/forms.py:121
|
||||
msgid "Import a ZIP file exported from WYGIWYH"
|
||||
msgstr "Importeer een ZIP-bestand geëxporteerd vanuit WYGIWYH"
|
||||
|
||||
#: apps/export_app/forms.py:122
|
||||
msgid "ZIP File"
|
||||
msgstr "ZIP-bestand"
|
||||
|
||||
#: apps/export_app/forms.py:138 apps/rules/models.py:16
|
||||
msgid "Transaction rules"
|
||||
msgstr "Verrichtingsregels"
|
||||
|
||||
#: apps/export_app/forms.py:140 apps/rules/models.py:53
|
||||
msgid "Edit transaction action"
|
||||
msgstr "Bewerk verrichtingsregel actie"
|
||||
|
||||
#: apps/export_app/forms.py:143 apps/rules/models.py:290
|
||||
msgid "Update or create transaction actions"
|
||||
msgstr "Bewerk of maak verrichtingsregel acties"
|
||||
|
||||
#: apps/export_app/forms.py:176 templates/cotton/transaction/item.html:158
|
||||
#: templates/cotton/ui/deleted_transactions_action_bar.html:47
|
||||
#: templates/export_app/fragments/restore.html:5
|
||||
#: templates/export_app/pages/index.html:24
|
||||
msgid "Restore"
|
||||
msgstr "Herstel"
|
||||
|
||||
#: apps/export_app/forms.py:187
|
||||
msgid "Please upload either a ZIP file or at least one CSV file"
|
||||
msgstr "Upload een ZIP-bestand of ten minste één CSV-bestand"
|
||||
|
||||
#: apps/export_app/views.py:168
|
||||
msgid "You have to select at least one export"
|
||||
msgstr "U moet ten minste één export selecteren"
|
||||
|
||||
#: apps/export_app/views.py:186
|
||||
msgid "Data restored successfully"
|
||||
msgstr "Gegevens succesvol hersteld"
|
||||
|
||||
#: apps/export_app/views.py:198
|
||||
msgid ""
|
||||
"There was an error restoring your data. Check the logs for more details."
|
||||
msgstr ""
|
||||
"Er is een fout opgetreden bij het herstellen van uw gegevens. Controleer de "
|
||||
"logboeken voor meer details."
|
||||
|
||||
#: apps/import_app/forms.py:49
|
||||
msgid "Select a file"
|
||||
msgstr "Selecteer een bestand"
|
||||
@@ -765,39 +878,51 @@ msgstr "Importrun met succes in de wachtrij geplaatst"
|
||||
msgid "Run deleted successfully"
|
||||
msgstr "Run met succes verwijderd"
|
||||
|
||||
#: apps/insights/utils/category_explorer.py:44
|
||||
#: apps/insights/utils/category_explorer.py:93 apps/transactions/models.py:170
|
||||
#: templates/calendar_view/fragments/list.html:42
|
||||
#: templates/calendar_view/fragments/list.html:44
|
||||
#: templates/calendar_view/fragments/list.html:52
|
||||
#: templates/calendar_view/fragments/list.html:54
|
||||
#: templates/cotton/ui/quick_transactions_buttons.html:10
|
||||
#: templates/insights/fragments/category_explorer/charts/account.html:54
|
||||
#: templates/insights/fragments/category_explorer/charts/currency.html:55
|
||||
#: templates/monthly_overview/fragments/monthly_summary.html:39
|
||||
msgid "Income"
|
||||
msgstr "Ontvangsten Transactie"
|
||||
|
||||
#: apps/insights/utils/category_explorer.py:48
|
||||
#: apps/insights/utils/category_explorer.py:97
|
||||
#: templates/insights/fragments/category_explorer/charts/account.html:60
|
||||
#: templates/insights/fragments/category_explorer/charts/currency.html:61
|
||||
#: templates/monthly_overview/fragments/monthly_summary.html:103
|
||||
msgid "Expenses"
|
||||
msgstr "Uitgaven"
|
||||
|
||||
#: apps/insights/utils/sankey.py:36 apps/insights/utils/sankey.py:167
|
||||
#, fuzzy
|
||||
#| msgid "Categories"
|
||||
#: apps/insights/forms.py:119 apps/insights/utils/sankey.py:36
|
||||
#: apps/insights/utils/sankey.py:167
|
||||
msgid "Uncategorized"
|
||||
msgstr "Categorieën"
|
||||
msgstr "Ongecategoriseerd"
|
||||
|
||||
#: apps/insights/utils/category_explorer.py:66
|
||||
#: apps/insights/utils/category_explorer.py:145
|
||||
#: templates/cotton/ui/percentage_distribution.html:10
|
||||
#: templates/cotton/ui/percentage_distribution.html:14
|
||||
#: templates/insights/fragments/category_explorer/charts/account.html:72
|
||||
#: templates/insights/fragments/category_explorer/charts/currency.html:72
|
||||
msgid "Current Income"
|
||||
msgstr "Huidige inkomsten"
|
||||
|
||||
#: apps/insights/utils/category_explorer.py:70
|
||||
#: apps/insights/utils/category_explorer.py:149
|
||||
#: templates/cotton/ui/percentage_distribution.html:24
|
||||
#: templates/cotton/ui/percentage_distribution.html:28
|
||||
#: templates/insights/fragments/category_explorer/charts/account.html:66
|
||||
#: templates/insights/fragments/category_explorer/charts/currency.html:66
|
||||
msgid "Current Expenses"
|
||||
msgstr "Huidige uitgaven"
|
||||
|
||||
#: apps/insights/utils/category_explorer.py:74
|
||||
#: apps/insights/utils/category_explorer.py:153
|
||||
#: templates/cotton/ui/percentage_distribution.html:3
|
||||
#: templates/cotton/ui/percentage_distribution.html:7
|
||||
#: templates/insights/fragments/category_explorer/charts/account.html:78
|
||||
#: templates/insights/fragments/category_explorer/charts/currency.html:78
|
||||
msgid "Projected Income"
|
||||
msgstr "Verwachte inkomsten"
|
||||
|
||||
#: apps/insights/utils/category_explorer.py:78
|
||||
#: apps/insights/utils/category_explorer.py:157
|
||||
#: templates/cotton/ui/percentage_distribution.html:17
|
||||
#: templates/cotton/ui/percentage_distribution.html:21
|
||||
#: templates/insights/fragments/category_explorer/charts/account.html:60
|
||||
#: templates/insights/fragments/category_explorer/charts/currency.html:60
|
||||
msgid "Projected Expenses"
|
||||
msgstr "Verwachte uitgaven"
|
||||
|
||||
#: apps/insights/utils/sankey.py:133 apps/insights/utils/sankey.py:134
|
||||
#: apps/insights/utils/sankey.py:263 apps/insights/utils/sankey.py:264
|
||||
#, fuzzy
|
||||
#| msgid "Save"
|
||||
msgid "Saved"
|
||||
msgstr "Opslaan"
|
||||
msgstr "Opgeslagen"
|
||||
|
||||
#: apps/rules/forms.py:20
|
||||
msgid "Run on creation"
|
||||
@@ -815,7 +940,7 @@ msgstr "Als..."
|
||||
msgid "Set field"
|
||||
msgstr "Veld instellen"
|
||||
|
||||
#: apps/rules/forms.py:65 templates/insights/fragments/sankey.html:90
|
||||
#: apps/rules/forms.py:65 templates/insights/fragments/sankey.html:94
|
||||
msgid "To"
|
||||
msgstr "Naar"
|
||||
|
||||
@@ -847,8 +972,8 @@ msgid "Paid"
|
||||
msgstr "Betaald"
|
||||
|
||||
#: apps/rules/forms.py:164 apps/rules/forms.py:177 apps/rules/models.py:28
|
||||
#: apps/rules/models.py:252 apps/transactions/forms.py:66
|
||||
#: apps/transactions/forms.py:322 apps/transactions/forms.py:492
|
||||
#: apps/rules/models.py:252 apps/transactions/forms.py:67
|
||||
#: apps/transactions/forms.py:323 apps/transactions/forms.py:493
|
||||
#: apps/transactions/models.py:187 apps/transactions/models.py:361
|
||||
#: apps/transactions/models.py:570
|
||||
msgid "Reference Date"
|
||||
@@ -856,13 +981,13 @@ msgstr "Referentiedatum"
|
||||
|
||||
#: apps/rules/forms.py:165 apps/rules/forms.py:178 apps/rules/models.py:29
|
||||
#: apps/rules/models.py:256 apps/transactions/models.py:192
|
||||
#: apps/transactions/models.py:551 templates/insights/fragments/sankey.html:91
|
||||
#: apps/transactions/models.py:551 templates/insights/fragments/sankey.html:95
|
||||
msgid "Amount"
|
||||
msgstr "Bedrag"
|
||||
|
||||
#: apps/rules/forms.py:166 apps/rules/forms.py:179 apps/rules/models.py:11
|
||||
#: apps/rules/models.py:30 apps/rules/models.py:260
|
||||
#: apps/transactions/forms.py:325 apps/transactions/models.py:197
|
||||
#: apps/transactions/forms.py:326 apps/transactions/models.py:197
|
||||
#: apps/transactions/models.py:345 apps/transactions/models.py:554
|
||||
msgid "Description"
|
||||
msgstr "Beschrijving"
|
||||
@@ -877,16 +1002,6 @@ msgstr "Interne opmerking"
|
||||
msgid "Internal ID"
|
||||
msgstr "Interne ID"
|
||||
|
||||
#: apps/rules/forms.py:172 apps/rules/forms.py:181 apps/rules/models.py:34
|
||||
#: apps/rules/models.py:276 apps/transactions/filters.py:81
|
||||
#: apps/transactions/forms.py:55 apps/transactions/forms.py:486
|
||||
#: apps/transactions/forms.py:731 apps/transactions/models.py:161
|
||||
#: apps/transactions/models.py:214 apps/transactions/models.py:383
|
||||
#: apps/transactions/models.py:565 templates/entities/fragments/list.html:5
|
||||
#: templates/entities/pages/index.html:4 templates/includes/navbar.html:110
|
||||
msgid "Entities"
|
||||
msgstr "Bedrijven"
|
||||
|
||||
#: apps/rules/forms.py:199
|
||||
msgid "Search Criteria"
|
||||
msgstr "Zoek Vereisten"
|
||||
@@ -903,10 +1018,6 @@ msgstr "Trigger"
|
||||
msgid "Transaction rule"
|
||||
msgstr "Verrichtingsregel"
|
||||
|
||||
#: apps/rules/models.py:16
|
||||
msgid "Transaction rules"
|
||||
msgstr "Verrichtingsregels"
|
||||
|
||||
#: apps/rules/models.py:40 apps/rules/models.py:78
|
||||
msgid "Rule"
|
||||
msgstr "Regel"
|
||||
@@ -919,10 +1030,6 @@ msgstr "Veld"
|
||||
msgid "Value"
|
||||
msgstr "Waarde"
|
||||
|
||||
#: apps/rules/models.py:53
|
||||
msgid "Edit transaction action"
|
||||
msgstr "Bewerk verrichtingsregel actie"
|
||||
|
||||
#: apps/rules/models.py:54
|
||||
msgid "Edit transaction actions"
|
||||
msgstr "Bewerk verrichtingsregel acties"
|
||||
@@ -968,22 +1075,17 @@ msgid "Filter"
|
||||
msgstr "Filter"
|
||||
|
||||
#: apps/rules/models.py:85
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"Generic expression to enable or disable execution. Should evaluate to True "
|
||||
"or False"
|
||||
msgstr ""
|
||||
"Generieke expressie om uitvoering in of uit te schakelen. Moet evalueren "
|
||||
"naar True of False"
|
||||
"naar Waar of Onwaar"
|
||||
|
||||
#: apps/rules/models.py:289
|
||||
msgid "Update or create transaction action"
|
||||
msgstr "Bewerk of maak verrichtingsregel actie"
|
||||
|
||||
#: apps/rules/models.py:290
|
||||
msgid "Update or create transaction actions"
|
||||
msgstr "Bewerk of maak verrichtingsregel acties"
|
||||
|
||||
#: apps/rules/views.py:52
|
||||
msgid "Rule deactivated successfully"
|
||||
msgstr "Regel succesvol uitgeschakeld"
|
||||
@@ -1039,11 +1141,6 @@ msgstr "Inhoud"
|
||||
msgid "Transaction Type"
|
||||
msgstr "Soort transactie"
|
||||
|
||||
#: apps/transactions/filters.py:67 templates/categories/fragments/list.html:5
|
||||
#: templates/categories/pages/index.html:4 templates/includes/navbar.html:106
|
||||
msgid "Categories"
|
||||
msgstr "Categorieën"
|
||||
|
||||
#: apps/transactions/filters.py:91
|
||||
msgid "Date from"
|
||||
msgstr "Datum vanaf"
|
||||
@@ -1064,40 +1161,40 @@ msgstr "Minimum bedrag"
|
||||
msgid "Amount max"
|
||||
msgstr "Maximaal bedrag"
|
||||
|
||||
#: apps/transactions/forms.py:158
|
||||
#: apps/transactions/forms.py:160
|
||||
msgid "More"
|
||||
msgstr "Meer"
|
||||
|
||||
#: apps/transactions/forms.py:278
|
||||
#: apps/transactions/forms.py:279
|
||||
msgid "From Amount"
|
||||
msgstr "Van Bedrag"
|
||||
|
||||
#: apps/transactions/forms.py:283
|
||||
#: apps/transactions/forms.py:284
|
||||
msgid "To Amount"
|
||||
msgstr "Naar Bedrag"
|
||||
|
||||
#: apps/transactions/forms.py:398
|
||||
#: apps/transactions/forms.py:399
|
||||
#: templates/cotton/ui/quick_transactions_buttons.html:40
|
||||
msgid "Transfer"
|
||||
msgstr "Overschrijving"
|
||||
|
||||
#: apps/transactions/forms.py:610
|
||||
#: apps/transactions/forms.py:611
|
||||
msgid "Tag name"
|
||||
msgstr "Labelnaam"
|
||||
|
||||
#: apps/transactions/forms.py:642
|
||||
#: apps/transactions/forms.py:643
|
||||
msgid "Entity name"
|
||||
msgstr "Naam van bedrijf"
|
||||
|
||||
#: apps/transactions/forms.py:674
|
||||
#: apps/transactions/forms.py:675
|
||||
msgid "Category name"
|
||||
msgstr "Naam van categorie"
|
||||
|
||||
#: apps/transactions/forms.py:676
|
||||
#: apps/transactions/forms.py:677
|
||||
msgid "Muted categories won't count towards your monthly total"
|
||||
msgstr "Gedempte categorieën tellen niet mee voor je maandtotaal"
|
||||
|
||||
#: apps/transactions/forms.py:846
|
||||
#: apps/transactions/forms.py:847
|
||||
msgid "End date should be after the start date"
|
||||
msgstr "De einddatum moet na de begindatum vallen"
|
||||
|
||||
@@ -1144,6 +1241,16 @@ msgstr ""
|
||||
msgid "Entity"
|
||||
msgstr "Bedrijf"
|
||||
|
||||
#: apps/transactions/models.py:170
|
||||
#: templates/calendar_view/fragments/list.html:42
|
||||
#: templates/calendar_view/fragments/list.html:44
|
||||
#: templates/calendar_view/fragments/list.html:52
|
||||
#: templates/calendar_view/fragments/list.html:54
|
||||
#: templates/cotton/ui/quick_transactions_buttons.html:10
|
||||
#: templates/monthly_overview/fragments/monthly_summary.html:39
|
||||
msgid "Income"
|
||||
msgstr "Ontvangsten Transactie"
|
||||
|
||||
#: apps/transactions/models.py:171
|
||||
#: templates/calendar_view/fragments/list.html:46
|
||||
#: templates/calendar_view/fragments/list.html:48
|
||||
@@ -1173,29 +1280,17 @@ msgstr "Verwijderd Op"
|
||||
msgid "Transaction"
|
||||
msgstr "Verrichting"
|
||||
|
||||
#: apps/transactions/models.py:256 templates/includes/navbar.html:57
|
||||
#: templates/includes/navbar.html:104
|
||||
#: templates/recurring_transactions/fragments/list_transactions.html:5
|
||||
#: templates/recurring_transactions/fragments/table.html:37
|
||||
#: templates/transactions/pages/transactions.html:5
|
||||
msgid "Transactions"
|
||||
msgstr "Verrichtingen"
|
||||
|
||||
#: apps/transactions/models.py:323 templates/tags/fragments/table.html:53
|
||||
msgid "No tags"
|
||||
msgstr "Geen labels"
|
||||
|
||||
#: apps/transactions/models.py:324
|
||||
#, fuzzy
|
||||
#| msgid "No categories"
|
||||
msgid "No category"
|
||||
msgstr "Geen categorieën"
|
||||
msgstr "Geen categorie"
|
||||
|
||||
#: apps/transactions/models.py:326
|
||||
#, fuzzy
|
||||
#| msgid "Description"
|
||||
msgid "No description"
|
||||
msgstr "Beschrijving"
|
||||
msgstr "Geen Beschrijving"
|
||||
|
||||
#: apps/transactions/models.py:332
|
||||
msgid "Yearly"
|
||||
@@ -1242,12 +1337,6 @@ msgstr "Terugkeerpatroon"
|
||||
msgid "Installment Amount"
|
||||
msgstr "Termijnbedrag"
|
||||
|
||||
#: apps/transactions/models.py:391 templates/includes/navbar.html:72
|
||||
#: templates/installment_plans/fragments/list.html:5
|
||||
#: templates/installment_plans/pages/index.html:4
|
||||
msgid "Installment Plans"
|
||||
msgstr "Afbetalingsplannen"
|
||||
|
||||
#: apps/transactions/models.py:533
|
||||
msgid "day(s)"
|
||||
msgstr "dag(en)"
|
||||
@@ -1285,12 +1374,6 @@ msgstr "Laatste Gegenereerde Datum"
|
||||
msgid "Last Generated Reference Date"
|
||||
msgstr "Laatste Gegenereerde Referentiedatum"
|
||||
|
||||
#: apps/transactions/models.py:592 templates/includes/navbar.html:74
|
||||
#: templates/recurring_transactions/fragments/list.html:5
|
||||
#: templates/recurring_transactions/pages/index.html:4
|
||||
msgid "Recurring Transactions"
|
||||
msgstr "Terugkerende Verrichtingen"
|
||||
|
||||
#: apps/transactions/validators.py:8
|
||||
#, python-format
|
||||
msgid "%(value)s has too many decimal places. Maximum is 30."
|
||||
@@ -1812,6 +1895,7 @@ msgstr "Sluiten"
|
||||
|
||||
#: templates/cotton/config/search.html:6
|
||||
#: templates/import_app/fragments/profiles/list_presets.html:13
|
||||
#: templates/monthly_overview/pages/overview.html:177
|
||||
msgid "Search"
|
||||
msgstr "Zoeken"
|
||||
|
||||
@@ -1819,20 +1903,11 @@ msgstr "Zoeken"
|
||||
msgid "Select"
|
||||
msgstr "Selecteer"
|
||||
|
||||
#: templates/cotton/transaction/item.html:56
|
||||
msgid "DCA"
|
||||
msgstr ""
|
||||
|
||||
#: templates/cotton/transaction/item.html:137
|
||||
#: templates/cotton/ui/transactions_action_bar.html:78
|
||||
msgid "Duplicate"
|
||||
msgstr "Dupliceren"
|
||||
|
||||
#: templates/cotton/transaction/item.html:158
|
||||
#: templates/cotton/ui/deleted_transactions_action_bar.html:47
|
||||
msgid "Restore"
|
||||
msgstr "Herstel"
|
||||
|
||||
#: templates/cotton/ui/account_card.html:15
|
||||
#: templates/cotton/ui/currency_card.html:10
|
||||
msgid "projected income"
|
||||
@@ -1936,26 +2011,6 @@ msgstr "Minimaal"
|
||||
msgid "Count"
|
||||
msgstr "Rekenen"
|
||||
|
||||
#: templates/cotton/ui/percentage_distribution.html:3
|
||||
#: templates/cotton/ui/percentage_distribution.html:7
|
||||
msgid "Projected Income"
|
||||
msgstr "Verwachte inkomsten"
|
||||
|
||||
#: templates/cotton/ui/percentage_distribution.html:10
|
||||
#: templates/cotton/ui/percentage_distribution.html:14
|
||||
msgid "Current Income"
|
||||
msgstr "Huidige inkomsten"
|
||||
|
||||
#: templates/cotton/ui/percentage_distribution.html:17
|
||||
#: templates/cotton/ui/percentage_distribution.html:21
|
||||
msgid "Projected Expenses"
|
||||
msgstr "Verwachte uitgaven"
|
||||
|
||||
#: templates/cotton/ui/percentage_distribution.html:24
|
||||
#: templates/cotton/ui/percentage_distribution.html:28
|
||||
msgid "Current Expenses"
|
||||
msgstr "Huidige uitgaven"
|
||||
|
||||
#: templates/cotton/ui/quick_transactions_buttons.html:25
|
||||
msgid "Installment"
|
||||
msgstr "Afbetaling"
|
||||
@@ -2156,12 +2211,6 @@ msgstr "Geen wisselkoersen"
|
||||
msgid "Page navigation"
|
||||
msgstr "Paginanavigatie"
|
||||
|
||||
#: templates/exchange_rates_services/fragments/list.html:6
|
||||
#: templates/exchange_rates_services/pages/index.html:4
|
||||
#: templates/includes/navbar.html:136
|
||||
msgid "Automatic Exchange Rates"
|
||||
msgstr "Automatische Wisselkoersen"
|
||||
|
||||
#: templates/exchange_rates_services/fragments/list.html:21
|
||||
msgid "Fetch all"
|
||||
msgstr "Alles Ophalen"
|
||||
@@ -2190,6 +2239,10 @@ msgstr "rekeningen"
|
||||
msgid "No services configured"
|
||||
msgstr "Geen diensten ingesteld"
|
||||
|
||||
#: templates/export_app/pages/index.html:4 templates/includes/navbar.html:136
|
||||
msgid "Export and Restore"
|
||||
msgstr "Exporteren en Herstellen"
|
||||
|
||||
#: templates/import_app/fragments/profiles/add.html:6
|
||||
msgid "Add new import profile"
|
||||
msgstr "Nieuw importprofiel toevoegen"
|
||||
@@ -2202,11 +2255,6 @@ msgstr "Een bericht van de auteur"
|
||||
msgid "Edit import profile"
|
||||
msgstr "Importprofiel bewerken"
|
||||
|
||||
#: templates/import_app/fragments/profiles/list.html:5
|
||||
#: templates/import_app/pages/profiles_index.html:4
|
||||
msgid "Import Profiles"
|
||||
msgstr "Profielen importeren"
|
||||
|
||||
#: templates/import_app/fragments/profiles/list.html:17
|
||||
msgid "New"
|
||||
msgstr "Nieuw"
|
||||
@@ -2298,7 +2346,7 @@ msgstr "Huidige"
|
||||
|
||||
#: templates/includes/navbar.html:50
|
||||
msgid "Insights"
|
||||
msgstr ""
|
||||
msgstr "Inzichten"
|
||||
|
||||
#: templates/includes/navbar.html:66
|
||||
msgid "Trash Can"
|
||||
@@ -2332,20 +2380,15 @@ msgstr "Beheer"
|
||||
msgid "Automation"
|
||||
msgstr "Automatisatie"
|
||||
|
||||
#: templates/includes/navbar.html:132 templates/rules/fragments/list.html:5
|
||||
#: templates/rules/pages/index.html:4
|
||||
msgid "Rules"
|
||||
msgstr "Regels"
|
||||
|
||||
#: templates/includes/navbar.html:146
|
||||
#: templates/includes/navbar.html:148
|
||||
msgid "Only use this if you know what you're doing"
|
||||
msgstr "Gebruik dit alleen als je weet wat je doet"
|
||||
|
||||
#: templates/includes/navbar.html:147
|
||||
#: templates/includes/navbar.html:149
|
||||
msgid "Django Admin"
|
||||
msgstr "Django Beheerder"
|
||||
|
||||
#: templates/includes/navbar.html:156
|
||||
#: templates/includes/navbar.html:158
|
||||
msgid "Calculator"
|
||||
msgstr "Rekenmachine"
|
||||
|
||||
@@ -2379,69 +2422,88 @@ msgstr "Annuleer"
|
||||
msgid "Confirm"
|
||||
msgstr "Bevestig"
|
||||
|
||||
#: templates/insights/fragments/category_explorer/index.html:13
|
||||
#: templates/insights/fragments/category_explorer/charts/account.html:100
|
||||
#: templates/insights/fragments/category_explorer/charts/currency.html:92
|
||||
#: templates/monthly_overview/fragments/monthly_account_summary.html:14
|
||||
#: templates/monthly_overview/fragments/monthly_currency_summary.html:13
|
||||
#: templates/transactions/fragments/all_account_summary.html:14
|
||||
#: templates/transactions/fragments/all_currency_summary.html:13
|
||||
#: templates/transactions/fragments/summary.html:27
|
||||
#: templates/transactions/fragments/summary.html:42
|
||||
#: templates/yearly_overview/fragments/account_data.html:12
|
||||
#: templates/yearly_overview/fragments/currency_data.html:12
|
||||
msgid "No information to display"
|
||||
msgstr "Geen informatie om weer te geven"
|
||||
|
||||
#: templates/insights/fragments/category_explorer/index.html:14
|
||||
msgid "Income/Expense by Account"
|
||||
msgstr ""
|
||||
msgstr "Inkomsten/uitgaven per rekening"
|
||||
|
||||
#: templates/insights/fragments/category_explorer/index.html:25
|
||||
#, fuzzy
|
||||
#| msgid "Exchange Currency"
|
||||
#: templates/insights/fragments/category_explorer/index.html:26
|
||||
msgid "Income/Expense by Currency"
|
||||
msgstr "Eenheid Wisselgeld"
|
||||
msgstr "Inkomsten/uitgaven per Munteenheid"
|
||||
|
||||
#: templates/insights/fragments/sankey.html:89
|
||||
#: templates/insights/fragments/late_transactions.html:15
|
||||
msgid "All good!"
|
||||
msgstr "Allemaal goed!"
|
||||
|
||||
#: templates/insights/fragments/late_transactions.html:16
|
||||
msgid "No late transactions"
|
||||
msgstr "Geen betalingsachterstanden"
|
||||
|
||||
#: templates/insights/fragments/latest_transactions.html:14
|
||||
msgid "No recent transactions"
|
||||
msgstr "Geen recente betalingen"
|
||||
|
||||
#: templates/insights/fragments/sankey.html:93
|
||||
msgid "From"
|
||||
msgstr ""
|
||||
msgstr "Van"
|
||||
|
||||
#: templates/insights/fragments/sankey.html:92
|
||||
#: templates/insights/fragments/sankey.html:96
|
||||
msgid "Percentage"
|
||||
msgstr ""
|
||||
msgstr "Percentage"
|
||||
|
||||
#: templates/insights/pages/index.html:33
|
||||
#, fuzzy
|
||||
#| msgid "Monthly"
|
||||
#: templates/insights/pages/index.html:35
|
||||
msgid "Month"
|
||||
msgstr "Maandelijks"
|
||||
msgstr "Maand"
|
||||
|
||||
#: templates/insights/pages/index.html:36
|
||||
#: templates/insights/pages/index.html:38
|
||||
#: templates/yearly_overview/pages/overview_by_account.html:61
|
||||
#: templates/yearly_overview/pages/overview_by_currency.html:63
|
||||
msgid "Year"
|
||||
msgstr "Jaar"
|
||||
|
||||
#: templates/insights/pages/index.html:39
|
||||
#, fuzzy
|
||||
#| msgid "Unchanged"
|
||||
#: templates/insights/pages/index.html:43
|
||||
msgid "Month Range"
|
||||
msgstr "Ongewijzigd"
|
||||
msgstr "Maand Bereik"
|
||||
|
||||
#: templates/insights/pages/index.html:42
|
||||
#: templates/insights/pages/index.html:48
|
||||
msgid "Year Range"
|
||||
msgstr ""
|
||||
msgstr "Jaar Bereik"
|
||||
|
||||
#: templates/insights/pages/index.html:45
|
||||
#, fuzzy
|
||||
#| msgid "Date and Time"
|
||||
#: templates/insights/pages/index.html:53
|
||||
msgid "Date Range"
|
||||
msgstr "Datum en Tijd"
|
||||
|
||||
#: templates/insights/pages/index.html:74
|
||||
#, fuzzy
|
||||
#| msgid "Account"
|
||||
msgid "Account Flow"
|
||||
msgstr "Rekening"
|
||||
msgstr "Datum Bereik"
|
||||
|
||||
#: templates/insights/pages/index.html:81
|
||||
#, fuzzy
|
||||
#| msgid "Currency Code"
|
||||
msgid "Currency Flow"
|
||||
msgstr "Munteenheids Code"
|
||||
msgid "Account Flow"
|
||||
msgstr "Rekeningstroom"
|
||||
|
||||
#: templates/insights/pages/index.html:88
|
||||
#, fuzzy
|
||||
#| msgid "Category name"
|
||||
msgid "Currency Flow"
|
||||
msgstr "Geldstroom"
|
||||
|
||||
#: templates/insights/pages/index.html:95
|
||||
msgid "Category Explorer"
|
||||
msgstr "Naam van categorie"
|
||||
msgstr "Categorie Verkenner"
|
||||
|
||||
#: templates/insights/pages/index.html:102
|
||||
msgid "Late Transactions"
|
||||
msgstr "Betalingsachterstanden"
|
||||
|
||||
#: templates/insights/pages/index.html:108
|
||||
msgid "Latest Transactions"
|
||||
msgstr "Laatste Verrichtingen"
|
||||
|
||||
#: templates/installment_plans/fragments/add.html:5
|
||||
msgid "Add installment plan"
|
||||
@@ -2512,17 +2574,6 @@ msgstr "Artikel"
|
||||
msgid "No transactions this month"
|
||||
msgstr "Geen verrichtingen deze maand"
|
||||
|
||||
#: templates/monthly_overview/fragments/monthly_account_summary.html:14
|
||||
#: templates/monthly_overview/fragments/monthly_currency_summary.html:13
|
||||
#: templates/transactions/fragments/all_account_summary.html:14
|
||||
#: templates/transactions/fragments/all_currency_summary.html:13
|
||||
#: templates/transactions/fragments/summary.html:27
|
||||
#: templates/transactions/fragments/summary.html:42
|
||||
#: templates/yearly_overview/fragments/account_data.html:12
|
||||
#: templates/yearly_overview/fragments/currency_data.html:12
|
||||
msgid "No information to display"
|
||||
msgstr "Geen informatie om weer te geven"
|
||||
|
||||
#: templates/monthly_overview/fragments/monthly_summary.html:6
|
||||
msgid "Daily Spending Allowance"
|
||||
msgstr "Dagelijks Toegestane Besteding"
|
||||
@@ -2543,6 +2594,10 @@ msgstr "actueel"
|
||||
msgid "projected"
|
||||
msgstr "verwacht"
|
||||
|
||||
#: templates/monthly_overview/fragments/monthly_summary.html:103
|
||||
msgid "Expenses"
|
||||
msgstr "Uitgaven"
|
||||
|
||||
#: templates/monthly_overview/fragments/monthly_summary.html:167
|
||||
msgid "Total"
|
||||
msgstr "Totaal"
|
||||
@@ -2812,6 +2867,31 @@ msgstr "Bedragen tonen"
|
||||
msgid "Yearly Overview"
|
||||
msgstr "Jaaroverzicht"
|
||||
|
||||
#, fuzzy
|
||||
#~| msgid "From Amount"
|
||||
#~ msgid "Principal Amount"
|
||||
#~ msgstr "Van Bedrag"
|
||||
|
||||
#, fuzzy
|
||||
#~| msgid "Interval"
|
||||
#~ msgid "Interest"
|
||||
#~ msgstr "Interval"
|
||||
|
||||
#, fuzzy
|
||||
#~| msgid "Management"
|
||||
#~ msgid "Loan Payment"
|
||||
#~ msgstr "Beheer"
|
||||
|
||||
#, fuzzy
|
||||
#~| msgid "Management"
|
||||
#~ msgid "Loan Payments"
|
||||
#~ msgstr "Beheer"
|
||||
|
||||
#, fuzzy
|
||||
#~| msgid "Installment Plans"
|
||||
#~ msgid "Installment Planss"
|
||||
#~ msgstr "Afbetalingsplannen"
|
||||
|
||||
#, fuzzy
|
||||
#~| msgid "Start Date"
|
||||
#~ msgid "Search Date"
|
||||
@@ -2867,11 +2947,6 @@ msgstr "Jaaroverzicht"
|
||||
#~ msgid "Reference Date Operator"
|
||||
#~ msgstr "Referentiedatum vanaf"
|
||||
|
||||
#, fuzzy
|
||||
#~| msgid "From Amount"
|
||||
#~ msgid "Search Amount"
|
||||
#~ msgstr "Van Bedrag"
|
||||
|
||||
#, fuzzy
|
||||
#~| msgid "Amount max"
|
||||
#~ msgid "Amount Operator"
|
||||
|
||||
@@ -8,8 +8,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-02-16 13:04-0300\n"
|
||||
"PO-Revision-Date: 2025-02-16 13:05-0300\n"
|
||||
"POT-Creation-Date: 2025-02-24 16:30-0300\n"
|
||||
"PO-Revision-Date: 2025-02-19 23:06-0300\n"
|
||||
"Last-Translator: Herculino Trotta\n"
|
||||
"Language-Team: \n"
|
||||
"Language: pt_BR\n"
|
||||
@@ -27,10 +27,10 @@ msgstr "Nome do grupo"
|
||||
#: apps/currencies/forms.py:53 apps/currencies/forms.py:91
|
||||
#: apps/currencies/forms.py:142 apps/dca/forms.py:49 apps/dca/forms.py:224
|
||||
#: apps/import_app/forms.py:34 apps/rules/forms.py:45 apps/rules/forms.py:87
|
||||
#: apps/rules/forms.py:359 apps/transactions/forms.py:190
|
||||
#: apps/transactions/forms.py:257 apps/transactions/forms.py:581
|
||||
#: apps/transactions/forms.py:624 apps/transactions/forms.py:656
|
||||
#: apps/transactions/forms.py:691 apps/transactions/forms.py:827
|
||||
#: apps/rules/forms.py:359 apps/transactions/forms.py:191
|
||||
#: apps/transactions/forms.py:258 apps/transactions/forms.py:582
|
||||
#: apps/transactions/forms.py:625 apps/transactions/forms.py:657
|
||||
#: apps/transactions/forms.py:692 apps/transactions/forms.py:828
|
||||
msgid "Update"
|
||||
msgstr "Atualizar"
|
||||
|
||||
@@ -39,10 +39,10 @@ msgstr "Atualizar"
|
||||
#: apps/currencies/forms.py:99 apps/currencies/forms.py:150
|
||||
#: apps/dca/forms.py:57 apps/dca/forms.py:232 apps/import_app/forms.py:42
|
||||
#: apps/rules/forms.py:53 apps/rules/forms.py:95 apps/rules/forms.py:367
|
||||
#: apps/transactions/forms.py:174 apps/transactions/forms.py:199
|
||||
#: apps/transactions/forms.py:589 apps/transactions/forms.py:632
|
||||
#: apps/transactions/forms.py:664 apps/transactions/forms.py:699
|
||||
#: apps/transactions/forms.py:835
|
||||
#: apps/transactions/forms.py:176 apps/transactions/forms.py:200
|
||||
#: apps/transactions/forms.py:590 apps/transactions/forms.py:633
|
||||
#: apps/transactions/forms.py:665 apps/transactions/forms.py:700
|
||||
#: apps/transactions/forms.py:836
|
||||
#: templates/account_groups/fragments/list.html:9
|
||||
#: templates/accounts/fragments/list.html:9
|
||||
#: templates/categories/fragments/list.html:9
|
||||
@@ -70,21 +70,22 @@ msgid "New balance"
|
||||
msgstr "Novo saldo"
|
||||
|
||||
#: apps/accounts/forms.py:119 apps/dca/forms.py:85 apps/dca/forms.py:92
|
||||
#: apps/insights/forms.py:117 apps/rules/forms.py:168 apps/rules/forms.py:183
|
||||
#: apps/insights/forms.py:118 apps/rules/forms.py:168 apps/rules/forms.py:183
|
||||
#: apps/rules/models.py:32 apps/rules/models.py:280
|
||||
#: apps/transactions/forms.py:39 apps/transactions/forms.py:291
|
||||
#: apps/transactions/forms.py:298 apps/transactions/forms.py:478
|
||||
#: apps/transactions/forms.py:723 apps/transactions/models.py:203
|
||||
#: apps/transactions/forms.py:39 apps/transactions/forms.py:292
|
||||
#: apps/transactions/forms.py:299 apps/transactions/forms.py:479
|
||||
#: apps/transactions/forms.py:724 apps/transactions/models.py:203
|
||||
#: apps/transactions/models.py:378 apps/transactions/models.py:558
|
||||
msgid "Category"
|
||||
msgstr "Categoria"
|
||||
|
||||
#: apps/accounts/forms.py:126 apps/dca/forms.py:101 apps/dca/forms.py:109
|
||||
#: apps/export_app/forms.py:38 apps/export_app/forms.py:127
|
||||
#: apps/rules/forms.py:171 apps/rules/forms.py:180 apps/rules/models.py:33
|
||||
#: apps/rules/models.py:284 apps/transactions/filters.py:74
|
||||
#: apps/transactions/forms.py:47 apps/transactions/forms.py:307
|
||||
#: apps/transactions/forms.py:315 apps/transactions/forms.py:471
|
||||
#: apps/transactions/forms.py:716 apps/transactions/models.py:209
|
||||
#: apps/transactions/forms.py:47 apps/transactions/forms.py:308
|
||||
#: apps/transactions/forms.py:316 apps/transactions/forms.py:472
|
||||
#: apps/transactions/forms.py:717 apps/transactions/models.py:209
|
||||
#: apps/transactions/models.py:380 apps/transactions/models.py:562
|
||||
#: templates/includes/navbar.html:108 templates/tags/fragments/list.html:5
|
||||
#: templates/tags/pages/index.html:4
|
||||
@@ -158,13 +159,14 @@ msgstr ""
|
||||
|
||||
#: apps/accounts/models.py:59 apps/rules/forms.py:160 apps/rules/forms.py:173
|
||||
#: apps/rules/models.py:24 apps/rules/models.py:236
|
||||
#: apps/transactions/forms.py:59 apps/transactions/forms.py:463
|
||||
#: apps/transactions/forms.py:708 apps/transactions/models.py:176
|
||||
#: apps/transactions/forms.py:59 apps/transactions/forms.py:464
|
||||
#: apps/transactions/forms.py:709 apps/transactions/models.py:176
|
||||
#: apps/transactions/models.py:338 apps/transactions/models.py:540
|
||||
msgid "Account"
|
||||
msgstr "Conta"
|
||||
|
||||
#: apps/accounts/models.py:60 apps/transactions/filters.py:53
|
||||
#: apps/accounts/models.py:60 apps/export_app/forms.py:14
|
||||
#: apps/export_app/forms.py:124 apps/transactions/filters.py:53
|
||||
#: templates/accounts/fragments/list.html:5
|
||||
#: templates/accounts/pages/index.html:4 templates/includes/navbar.html:114
|
||||
#: templates/includes/navbar.html:116
|
||||
@@ -335,12 +337,12 @@ msgstr "Informação"
|
||||
msgid "Cache cleared successfully"
|
||||
msgstr "Cache limpo com sucesso"
|
||||
|
||||
#: apps/common/widgets/datepicker.py:47 apps/common/widgets/datepicker.py:186
|
||||
#: apps/common/widgets/datepicker.py:244
|
||||
#: apps/common/widgets/datepicker.py:53 apps/common/widgets/datepicker.py:206
|
||||
#: apps/common/widgets/datepicker.py:264
|
||||
msgid "Today"
|
||||
msgstr "Hoje"
|
||||
|
||||
#: apps/common/widgets/datepicker.py:123
|
||||
#: apps/common/widgets/datepicker.py:139
|
||||
msgid "Now"
|
||||
msgstr "Agora"
|
||||
|
||||
@@ -369,7 +371,7 @@ msgstr "Sufixo"
|
||||
|
||||
#: apps/currencies/forms.py:69 apps/dca/models.py:156 apps/rules/forms.py:163
|
||||
#: apps/rules/forms.py:176 apps/rules/models.py:27 apps/rules/models.py:248
|
||||
#: apps/transactions/forms.py:63 apps/transactions/forms.py:319
|
||||
#: apps/transactions/forms.py:63 apps/transactions/forms.py:320
|
||||
#: apps/transactions/models.py:186
|
||||
#: templates/dca/fragments/strategy/details.html:52
|
||||
#: templates/exchange_rates/fragments/table.html:10
|
||||
@@ -389,7 +391,8 @@ msgstr "Nome da Moeda"
|
||||
msgid "Decimal Places"
|
||||
msgstr "Casas Decimais"
|
||||
|
||||
#: apps/currencies/models.py:40 apps/transactions/filters.py:60
|
||||
#: apps/currencies/models.py:40 apps/export_app/forms.py:20
|
||||
#: apps/export_app/forms.py:125 apps/transactions/filters.py:60
|
||||
#: templates/currencies/fragments/list.html:5
|
||||
#: templates/currencies/pages/index.html:4 templates/includes/navbar.html:122
|
||||
#: templates/includes/navbar.html:124
|
||||
@@ -419,7 +422,8 @@ msgstr "Taxa de Câmbio"
|
||||
msgid "Date and Time"
|
||||
msgstr "Data e Tempo"
|
||||
|
||||
#: apps/currencies/models.py:74 templates/exchange_rates/fragments/list.html:6
|
||||
#: apps/currencies/models.py:74 apps/export_app/forms.py:62
|
||||
#: apps/export_app/forms.py:137 templates/exchange_rates/fragments/list.html:6
|
||||
#: templates/exchange_rates/pages/index.html:4
|
||||
#: templates/includes/navbar.html:126
|
||||
msgid "Exchange Rates"
|
||||
@@ -580,11 +584,11 @@ msgstr "Serviços marcados para execução com sucesso"
|
||||
msgid "Create transaction"
|
||||
msgstr "Criar transação"
|
||||
|
||||
#: apps/dca/forms.py:70 apps/transactions/forms.py:266
|
||||
#: apps/dca/forms.py:70 apps/transactions/forms.py:267
|
||||
msgid "From Account"
|
||||
msgstr "Conta de origem"
|
||||
|
||||
#: apps/dca/forms.py:76 apps/transactions/forms.py:271
|
||||
#: apps/dca/forms.py:76 apps/transactions/forms.py:272
|
||||
msgid "To Account"
|
||||
msgstr "Conta de destino"
|
||||
|
||||
@@ -609,7 +613,7 @@ msgstr "Conectar transação"
|
||||
msgid "You must provide an account."
|
||||
msgstr "Você deve informar uma conta."
|
||||
|
||||
#: apps/dca/forms.py:294 apps/transactions/forms.py:413
|
||||
#: apps/dca/forms.py:294 apps/transactions/forms.py:414
|
||||
msgid "From and To accounts must be different."
|
||||
msgstr "As contas De e Para devem ser diferentes."
|
||||
|
||||
@@ -628,7 +632,7 @@ msgstr "Moeda de pagamento"
|
||||
|
||||
#: apps/dca/models.py:27 apps/dca/models.py:179 apps/rules/forms.py:167
|
||||
#: apps/rules/forms.py:182 apps/rules/models.py:31 apps/rules/models.py:264
|
||||
#: apps/transactions/forms.py:333 apps/transactions/models.py:199
|
||||
#: apps/transactions/forms.py:334 apps/transactions/models.py:199
|
||||
#: apps/transactions/models.py:387 apps/transactions/models.py:568
|
||||
msgid "Notes"
|
||||
msgstr "Notas"
|
||||
@@ -637,7 +641,7 @@ msgstr "Notas"
|
||||
msgid "DCA Strategy"
|
||||
msgstr "Estratégia CMP"
|
||||
|
||||
#: apps/dca/models.py:33
|
||||
#: apps/dca/models.py:33 apps/export_app/forms.py:145
|
||||
msgid "DCA Strategies"
|
||||
msgstr "Estratégias CMP"
|
||||
|
||||
@@ -657,7 +661,7 @@ msgstr "Quantia recebida"
|
||||
msgid "DCA Entry"
|
||||
msgstr "Entrada CMP"
|
||||
|
||||
#: apps/dca/models.py:185
|
||||
#: apps/dca/models.py:185 apps/export_app/forms.py:146
|
||||
msgid "DCA Entries"
|
||||
msgstr "Entradas CMP"
|
||||
|
||||
@@ -685,6 +689,119 @@ msgstr "Entrada atualizada com sucesso"
|
||||
msgid "Entry deleted successfully"
|
||||
msgstr "Entrada apagada com sucesso"
|
||||
|
||||
#: apps/export_app/forms.py:26 apps/export_app/forms.py:129
|
||||
#: apps/transactions/models.py:256 templates/includes/navbar.html:57
|
||||
#: templates/includes/navbar.html:104
|
||||
#: templates/recurring_transactions/fragments/list_transactions.html:5
|
||||
#: templates/recurring_transactions/fragments/table.html:37
|
||||
#: templates/transactions/pages/transactions.html:5
|
||||
msgid "Transactions"
|
||||
msgstr "Transações"
|
||||
|
||||
#: apps/export_app/forms.py:32 apps/export_app/forms.py:126
|
||||
#: apps/transactions/filters.py:67 templates/categories/fragments/list.html:5
|
||||
#: templates/categories/pages/index.html:4 templates/includes/navbar.html:106
|
||||
msgid "Categories"
|
||||
msgstr "Categorias"
|
||||
|
||||
#: apps/export_app/forms.py:44 apps/export_app/forms.py:128
|
||||
#: apps/rules/forms.py:172 apps/rules/forms.py:181 apps/rules/models.py:34
|
||||
#: apps/rules/models.py:276 apps/transactions/filters.py:81
|
||||
#: apps/transactions/forms.py:55 apps/transactions/forms.py:487
|
||||
#: apps/transactions/forms.py:732 apps/transactions/models.py:161
|
||||
#: apps/transactions/models.py:214 apps/transactions/models.py:383
|
||||
#: apps/transactions/models.py:565 templates/entities/fragments/list.html:5
|
||||
#: templates/entities/pages/index.html:4 templates/includes/navbar.html:110
|
||||
msgid "Entities"
|
||||
msgstr "Entidades"
|
||||
|
||||
#: apps/export_app/forms.py:50 apps/export_app/forms.py:132
|
||||
#: apps/transactions/models.py:592 templates/includes/navbar.html:74
|
||||
#: templates/recurring_transactions/fragments/list.html:5
|
||||
#: templates/recurring_transactions/pages/index.html:4
|
||||
msgid "Recurring Transactions"
|
||||
msgstr "Transações Recorrentes"
|
||||
|
||||
#: apps/export_app/forms.py:56 apps/export_app/forms.py:130
|
||||
#: apps/transactions/models.py:391 templates/includes/navbar.html:72
|
||||
#: templates/installment_plans/fragments/list.html:5
|
||||
#: templates/installment_plans/pages/index.html:4
|
||||
msgid "Installment Plans"
|
||||
msgstr "Parcelamentos"
|
||||
|
||||
#: apps/export_app/forms.py:68 apps/export_app/forms.py:135
|
||||
#: templates/exchange_rates_services/fragments/list.html:6
|
||||
#: templates/exchange_rates_services/pages/index.html:4
|
||||
#: templates/includes/navbar.html:138
|
||||
msgid "Automatic Exchange Rates"
|
||||
msgstr "Taxas de Câmbio Automáticas"
|
||||
|
||||
#: apps/export_app/forms.py:74 templates/includes/navbar.html:132
|
||||
#: templates/rules/fragments/list.html:5 templates/rules/pages/index.html:4
|
||||
msgid "Rules"
|
||||
msgstr "Regras"
|
||||
|
||||
#: apps/export_app/forms.py:80 templates/cotton/transaction/item.html:56
|
||||
msgid "DCA"
|
||||
msgstr "CMP"
|
||||
|
||||
#: apps/export_app/forms.py:86 apps/export_app/forms.py:147
|
||||
#: templates/import_app/fragments/profiles/list.html:5
|
||||
#: templates/import_app/pages/profiles_index.html:4
|
||||
msgid "Import Profiles"
|
||||
msgstr "Perfis de Importação"
|
||||
|
||||
#: apps/export_app/forms.py:112 templates/export_app/fragments/export.html:5
|
||||
#: templates/export_app/pages/index.html:15
|
||||
msgid "Export"
|
||||
msgstr "Exportar"
|
||||
|
||||
#: apps/export_app/forms.py:121
|
||||
msgid "Import a ZIP file exported from WYGIWYH"
|
||||
msgstr "Importe um arquivo ZIP exportado do WYGIWYH"
|
||||
|
||||
#: apps/export_app/forms.py:122
|
||||
msgid "ZIP File"
|
||||
msgstr "Arquivo ZIP"
|
||||
|
||||
#: apps/export_app/forms.py:138 apps/rules/models.py:16
|
||||
msgid "Transaction rules"
|
||||
msgstr "Regra da transação"
|
||||
|
||||
#: apps/export_app/forms.py:140 apps/rules/models.py:53
|
||||
msgid "Edit transaction action"
|
||||
msgstr "Ação de editar de transação"
|
||||
|
||||
#: apps/export_app/forms.py:143 apps/rules/models.py:290
|
||||
msgid "Update or create transaction actions"
|
||||
msgstr "Ações de atualizar ou criar transação"
|
||||
|
||||
#: apps/export_app/forms.py:176 templates/cotton/transaction/item.html:158
|
||||
#: templates/cotton/ui/deleted_transactions_action_bar.html:47
|
||||
#: templates/export_app/fragments/restore.html:5
|
||||
#: templates/export_app/pages/index.html:24
|
||||
msgid "Restore"
|
||||
msgstr "Restaurar"
|
||||
|
||||
#: apps/export_app/forms.py:187
|
||||
msgid "Please upload either a ZIP file or at least one CSV file"
|
||||
msgstr "Carregue um arquivo ZIP ou pelo menos um arquivo CSV"
|
||||
|
||||
#: apps/export_app/views.py:168
|
||||
msgid "You have to select at least one export"
|
||||
msgstr "É necessário selecionar pelo menos uma exportação"
|
||||
|
||||
#: apps/export_app/views.py:186
|
||||
msgid "Data restored successfully"
|
||||
msgstr "Dados restaurados com sucesso"
|
||||
|
||||
#: apps/export_app/views.py:198
|
||||
msgid ""
|
||||
"There was an error restoring your data. Check the logs for more details."
|
||||
msgstr ""
|
||||
"Ocorreu um erro ao restaurar seus dados. Verifique o log para obter mais "
|
||||
"detalhes."
|
||||
|
||||
#: apps/import_app/forms.py:49
|
||||
msgid "Select a file"
|
||||
msgstr "Selecione um arquivo"
|
||||
@@ -759,31 +876,47 @@ msgstr "Importação adicionada à fila com sucesso"
|
||||
msgid "Run deleted successfully"
|
||||
msgstr "Importação apagada com sucesso"
|
||||
|
||||
#: apps/insights/utils/category_explorer.py:44
|
||||
#: apps/insights/utils/category_explorer.py:93 apps/transactions/models.py:170
|
||||
#: templates/calendar_view/fragments/list.html:42
|
||||
#: templates/calendar_view/fragments/list.html:44
|
||||
#: templates/calendar_view/fragments/list.html:52
|
||||
#: templates/calendar_view/fragments/list.html:54
|
||||
#: templates/cotton/ui/quick_transactions_buttons.html:10
|
||||
#: templates/insights/fragments/category_explorer/charts/account.html:54
|
||||
#: templates/insights/fragments/category_explorer/charts/currency.html:55
|
||||
#: templates/monthly_overview/fragments/monthly_summary.html:39
|
||||
msgid "Income"
|
||||
msgstr "Renda"
|
||||
|
||||
#: apps/insights/utils/category_explorer.py:48
|
||||
#: apps/insights/utils/category_explorer.py:97
|
||||
#: templates/insights/fragments/category_explorer/charts/account.html:60
|
||||
#: templates/insights/fragments/category_explorer/charts/currency.html:61
|
||||
#: templates/monthly_overview/fragments/monthly_summary.html:103
|
||||
msgid "Expenses"
|
||||
msgstr "Despesas"
|
||||
|
||||
#: apps/insights/utils/sankey.py:36 apps/insights/utils/sankey.py:167
|
||||
#: apps/insights/forms.py:119 apps/insights/utils/sankey.py:36
|
||||
#: apps/insights/utils/sankey.py:167
|
||||
msgid "Uncategorized"
|
||||
msgstr "Sem categoria"
|
||||
|
||||
#: apps/insights/utils/category_explorer.py:66
|
||||
#: apps/insights/utils/category_explorer.py:145
|
||||
#: templates/cotton/ui/percentage_distribution.html:10
|
||||
#: templates/cotton/ui/percentage_distribution.html:14
|
||||
#: templates/insights/fragments/category_explorer/charts/account.html:72
|
||||
#: templates/insights/fragments/category_explorer/charts/currency.html:72
|
||||
msgid "Current Income"
|
||||
msgstr "Renda Atual"
|
||||
|
||||
#: apps/insights/utils/category_explorer.py:70
|
||||
#: apps/insights/utils/category_explorer.py:149
|
||||
#: templates/cotton/ui/percentage_distribution.html:24
|
||||
#: templates/cotton/ui/percentage_distribution.html:28
|
||||
#: templates/insights/fragments/category_explorer/charts/account.html:66
|
||||
#: templates/insights/fragments/category_explorer/charts/currency.html:66
|
||||
msgid "Current Expenses"
|
||||
msgstr "Despesas Atuais"
|
||||
|
||||
#: apps/insights/utils/category_explorer.py:74
|
||||
#: apps/insights/utils/category_explorer.py:153
|
||||
#: templates/cotton/ui/percentage_distribution.html:3
|
||||
#: templates/cotton/ui/percentage_distribution.html:7
|
||||
#: templates/insights/fragments/category_explorer/charts/account.html:78
|
||||
#: templates/insights/fragments/category_explorer/charts/currency.html:78
|
||||
msgid "Projected Income"
|
||||
msgstr "Renda Prevista"
|
||||
|
||||
#: apps/insights/utils/category_explorer.py:78
|
||||
#: apps/insights/utils/category_explorer.py:157
|
||||
#: templates/cotton/ui/percentage_distribution.html:17
|
||||
#: templates/cotton/ui/percentage_distribution.html:21
|
||||
#: templates/insights/fragments/category_explorer/charts/account.html:60
|
||||
#: templates/insights/fragments/category_explorer/charts/currency.html:60
|
||||
msgid "Projected Expenses"
|
||||
msgstr "Despesas Previstas"
|
||||
|
||||
#: apps/insights/utils/sankey.py:133 apps/insights/utils/sankey.py:134
|
||||
#: apps/insights/utils/sankey.py:263 apps/insights/utils/sankey.py:264
|
||||
msgid "Saved"
|
||||
@@ -805,7 +938,7 @@ msgstr "Se..."
|
||||
msgid "Set field"
|
||||
msgstr "Definir campo"
|
||||
|
||||
#: apps/rules/forms.py:65 templates/insights/fragments/sankey.html:90
|
||||
#: apps/rules/forms.py:65 templates/insights/fragments/sankey.html:94
|
||||
msgid "To"
|
||||
msgstr "Para"
|
||||
|
||||
@@ -837,8 +970,8 @@ msgid "Paid"
|
||||
msgstr "Pago"
|
||||
|
||||
#: apps/rules/forms.py:164 apps/rules/forms.py:177 apps/rules/models.py:28
|
||||
#: apps/rules/models.py:252 apps/transactions/forms.py:66
|
||||
#: apps/transactions/forms.py:322 apps/transactions/forms.py:492
|
||||
#: apps/rules/models.py:252 apps/transactions/forms.py:67
|
||||
#: apps/transactions/forms.py:323 apps/transactions/forms.py:493
|
||||
#: apps/transactions/models.py:187 apps/transactions/models.py:361
|
||||
#: apps/transactions/models.py:570
|
||||
msgid "Reference Date"
|
||||
@@ -846,13 +979,13 @@ msgstr "Data de Referência"
|
||||
|
||||
#: apps/rules/forms.py:165 apps/rules/forms.py:178 apps/rules/models.py:29
|
||||
#: apps/rules/models.py:256 apps/transactions/models.py:192
|
||||
#: apps/transactions/models.py:551 templates/insights/fragments/sankey.html:91
|
||||
#: apps/transactions/models.py:551 templates/insights/fragments/sankey.html:95
|
||||
msgid "Amount"
|
||||
msgstr "Quantia"
|
||||
|
||||
#: apps/rules/forms.py:166 apps/rules/forms.py:179 apps/rules/models.py:11
|
||||
#: apps/rules/models.py:30 apps/rules/models.py:260
|
||||
#: apps/transactions/forms.py:325 apps/transactions/models.py:197
|
||||
#: apps/transactions/forms.py:326 apps/transactions/models.py:197
|
||||
#: apps/transactions/models.py:345 apps/transactions/models.py:554
|
||||
msgid "Description"
|
||||
msgstr "Descrição"
|
||||
@@ -867,16 +1000,6 @@ msgstr "Nota Interna"
|
||||
msgid "Internal ID"
|
||||
msgstr "ID Interna"
|
||||
|
||||
#: apps/rules/forms.py:172 apps/rules/forms.py:181 apps/rules/models.py:34
|
||||
#: apps/rules/models.py:276 apps/transactions/filters.py:81
|
||||
#: apps/transactions/forms.py:55 apps/transactions/forms.py:486
|
||||
#: apps/transactions/forms.py:731 apps/transactions/models.py:161
|
||||
#: apps/transactions/models.py:214 apps/transactions/models.py:383
|
||||
#: apps/transactions/models.py:565 templates/entities/fragments/list.html:5
|
||||
#: templates/entities/pages/index.html:4 templates/includes/navbar.html:110
|
||||
msgid "Entities"
|
||||
msgstr "Entidades"
|
||||
|
||||
#: apps/rules/forms.py:199
|
||||
msgid "Search Criteria"
|
||||
msgstr "Critério de Busca"
|
||||
@@ -893,10 +1016,6 @@ msgstr "Gatilho"
|
||||
msgid "Transaction rule"
|
||||
msgstr "Regra da transação"
|
||||
|
||||
#: apps/rules/models.py:16
|
||||
msgid "Transaction rules"
|
||||
msgstr "Regra da transação"
|
||||
|
||||
#: apps/rules/models.py:40 apps/rules/models.py:78
|
||||
msgid "Rule"
|
||||
msgstr "Regra"
|
||||
@@ -909,13 +1028,9 @@ msgstr "Campo"
|
||||
msgid "Value"
|
||||
msgstr "Valor"
|
||||
|
||||
#: apps/rules/models.py:53
|
||||
msgid "Edit transaction action"
|
||||
msgstr "Editar ação de transação"
|
||||
|
||||
#: apps/rules/models.py:54
|
||||
msgid "Edit transaction actions"
|
||||
msgstr "Editar ações de transação"
|
||||
msgstr "Ações de editar de transação"
|
||||
|
||||
#: apps/rules/models.py:64
|
||||
msgid "is exactly"
|
||||
@@ -967,11 +1082,7 @@ msgstr ""
|
||||
|
||||
#: apps/rules/models.py:289
|
||||
msgid "Update or create transaction action"
|
||||
msgstr "Atualizar ou criar transação ação"
|
||||
|
||||
#: apps/rules/models.py:290
|
||||
msgid "Update or create transaction actions"
|
||||
msgstr "Atualizar ou criar transação ações"
|
||||
msgstr "Ação de atualizar ou criar transação"
|
||||
|
||||
#: apps/rules/views.py:52
|
||||
msgid "Rule deactivated successfully"
|
||||
@@ -1028,11 +1139,6 @@ msgstr "Conteúdo"
|
||||
msgid "Transaction Type"
|
||||
msgstr "Tipo de Transação"
|
||||
|
||||
#: apps/transactions/filters.py:67 templates/categories/fragments/list.html:5
|
||||
#: templates/categories/pages/index.html:4 templates/includes/navbar.html:106
|
||||
msgid "Categories"
|
||||
msgstr "Categorias"
|
||||
|
||||
#: apps/transactions/filters.py:91
|
||||
msgid "Date from"
|
||||
msgstr "Data de"
|
||||
@@ -1053,40 +1159,40 @@ msgstr "Quantia miníma"
|
||||
msgid "Amount max"
|
||||
msgstr "Quantia máxima"
|
||||
|
||||
#: apps/transactions/forms.py:158
|
||||
#: apps/transactions/forms.py:160
|
||||
msgid "More"
|
||||
msgstr "Mais"
|
||||
|
||||
#: apps/transactions/forms.py:278
|
||||
#: apps/transactions/forms.py:279
|
||||
msgid "From Amount"
|
||||
msgstr "Quantia de origem"
|
||||
|
||||
#: apps/transactions/forms.py:283
|
||||
#: apps/transactions/forms.py:284
|
||||
msgid "To Amount"
|
||||
msgstr "Quantia de destino"
|
||||
|
||||
#: apps/transactions/forms.py:398
|
||||
#: apps/transactions/forms.py:399
|
||||
#: templates/cotton/ui/quick_transactions_buttons.html:40
|
||||
msgid "Transfer"
|
||||
msgstr "Transferir"
|
||||
|
||||
#: apps/transactions/forms.py:610
|
||||
#: apps/transactions/forms.py:611
|
||||
msgid "Tag name"
|
||||
msgstr "Nome da Tag"
|
||||
|
||||
#: apps/transactions/forms.py:642
|
||||
#: apps/transactions/forms.py:643
|
||||
msgid "Entity name"
|
||||
msgstr "Nome da entidade"
|
||||
|
||||
#: apps/transactions/forms.py:674
|
||||
#: apps/transactions/forms.py:675
|
||||
msgid "Category name"
|
||||
msgstr "Nome da Categoria"
|
||||
|
||||
#: apps/transactions/forms.py:676
|
||||
#: apps/transactions/forms.py:677
|
||||
msgid "Muted categories won't count towards your monthly total"
|
||||
msgstr "As categorias silenciadas não serão contabilizadas em seu total mensal"
|
||||
|
||||
#: apps/transactions/forms.py:846
|
||||
#: apps/transactions/forms.py:847
|
||||
msgid "End date should be after the start date"
|
||||
msgstr "Data final deve ser após data inicial"
|
||||
|
||||
@@ -1132,6 +1238,16 @@ msgstr ""
|
||||
msgid "Entity"
|
||||
msgstr "Entidade"
|
||||
|
||||
#: apps/transactions/models.py:170
|
||||
#: templates/calendar_view/fragments/list.html:42
|
||||
#: templates/calendar_view/fragments/list.html:44
|
||||
#: templates/calendar_view/fragments/list.html:52
|
||||
#: templates/calendar_view/fragments/list.html:54
|
||||
#: templates/cotton/ui/quick_transactions_buttons.html:10
|
||||
#: templates/monthly_overview/fragments/monthly_summary.html:39
|
||||
msgid "Income"
|
||||
msgstr "Renda"
|
||||
|
||||
#: apps/transactions/models.py:171
|
||||
#: templates/calendar_view/fragments/list.html:46
|
||||
#: templates/calendar_view/fragments/list.html:48
|
||||
@@ -1161,14 +1277,6 @@ msgstr "Apagado Em"
|
||||
msgid "Transaction"
|
||||
msgstr "Transação"
|
||||
|
||||
#: apps/transactions/models.py:256 templates/includes/navbar.html:57
|
||||
#: templates/includes/navbar.html:104
|
||||
#: templates/recurring_transactions/fragments/list_transactions.html:5
|
||||
#: templates/recurring_transactions/fragments/table.html:37
|
||||
#: templates/transactions/pages/transactions.html:5
|
||||
msgid "Transactions"
|
||||
msgstr "Transações"
|
||||
|
||||
#: apps/transactions/models.py:323 templates/tags/fragments/table.html:53
|
||||
msgid "No tags"
|
||||
msgstr "Nenhuma tag"
|
||||
@@ -1226,12 +1334,6 @@ msgstr "Recorrência"
|
||||
msgid "Installment Amount"
|
||||
msgstr "Valor da Parcela"
|
||||
|
||||
#: apps/transactions/models.py:391 templates/includes/navbar.html:72
|
||||
#: templates/installment_plans/fragments/list.html:5
|
||||
#: templates/installment_plans/pages/index.html:4
|
||||
msgid "Installment Plans"
|
||||
msgstr "Parcelamentos"
|
||||
|
||||
#: apps/transactions/models.py:533
|
||||
msgid "day(s)"
|
||||
msgstr "dia(s)"
|
||||
@@ -1269,12 +1371,6 @@ msgstr "Última data gerada"
|
||||
msgid "Last Generated Reference Date"
|
||||
msgstr "Última data de referência gerada"
|
||||
|
||||
#: apps/transactions/models.py:592 templates/includes/navbar.html:74
|
||||
#: templates/recurring_transactions/fragments/list.html:5
|
||||
#: templates/recurring_transactions/pages/index.html:4
|
||||
msgid "Recurring Transactions"
|
||||
msgstr "Transações Recorrentes"
|
||||
|
||||
#: apps/transactions/validators.py:8
|
||||
#, python-format
|
||||
msgid "%(value)s has too many decimal places. Maximum is 30."
|
||||
@@ -1796,6 +1892,7 @@ msgstr "Fechar"
|
||||
|
||||
#: templates/cotton/config/search.html:6
|
||||
#: templates/import_app/fragments/profiles/list_presets.html:13
|
||||
#: templates/monthly_overview/pages/overview.html:177
|
||||
msgid "Search"
|
||||
msgstr "Buscar"
|
||||
|
||||
@@ -1803,20 +1900,11 @@ msgstr "Buscar"
|
||||
msgid "Select"
|
||||
msgstr "Selecionar"
|
||||
|
||||
#: templates/cotton/transaction/item.html:56
|
||||
msgid "DCA"
|
||||
msgstr "CMP"
|
||||
|
||||
#: templates/cotton/transaction/item.html:137
|
||||
#: templates/cotton/ui/transactions_action_bar.html:78
|
||||
msgid "Duplicate"
|
||||
msgstr "Duplicar"
|
||||
|
||||
#: templates/cotton/transaction/item.html:158
|
||||
#: templates/cotton/ui/deleted_transactions_action_bar.html:47
|
||||
msgid "Restore"
|
||||
msgstr "Restaurar"
|
||||
|
||||
#: templates/cotton/ui/account_card.html:15
|
||||
#: templates/cotton/ui/currency_card.html:10
|
||||
msgid "projected income"
|
||||
@@ -1920,26 +2008,6 @@ msgstr "Minímo"
|
||||
msgid "Count"
|
||||
msgstr "Contagem"
|
||||
|
||||
#: templates/cotton/ui/percentage_distribution.html:3
|
||||
#: templates/cotton/ui/percentage_distribution.html:7
|
||||
msgid "Projected Income"
|
||||
msgstr "Renda Prevista"
|
||||
|
||||
#: templates/cotton/ui/percentage_distribution.html:10
|
||||
#: templates/cotton/ui/percentage_distribution.html:14
|
||||
msgid "Current Income"
|
||||
msgstr "Renda Atual"
|
||||
|
||||
#: templates/cotton/ui/percentage_distribution.html:17
|
||||
#: templates/cotton/ui/percentage_distribution.html:21
|
||||
msgid "Projected Expenses"
|
||||
msgstr "Despesas Previstas"
|
||||
|
||||
#: templates/cotton/ui/percentage_distribution.html:24
|
||||
#: templates/cotton/ui/percentage_distribution.html:28
|
||||
msgid "Current Expenses"
|
||||
msgstr "Despesas Atuais"
|
||||
|
||||
#: templates/cotton/ui/quick_transactions_buttons.html:25
|
||||
msgid "Installment"
|
||||
msgstr "Parcelamento"
|
||||
@@ -2141,12 +2209,6 @@ msgstr "Nenhuma taxa de câmbio"
|
||||
msgid "Page navigation"
|
||||
msgstr "Navegação por página"
|
||||
|
||||
#: templates/exchange_rates_services/fragments/list.html:6
|
||||
#: templates/exchange_rates_services/pages/index.html:4
|
||||
#: templates/includes/navbar.html:136
|
||||
msgid "Automatic Exchange Rates"
|
||||
msgstr "Taxas de Câmbio Automáticas"
|
||||
|
||||
#: templates/exchange_rates_services/fragments/list.html:21
|
||||
msgid "Fetch all"
|
||||
msgstr "Executar todos"
|
||||
@@ -2175,6 +2237,10 @@ msgstr "contas"
|
||||
msgid "No services configured"
|
||||
msgstr "Nenhum serviço configurado"
|
||||
|
||||
#: templates/export_app/pages/index.html:4 templates/includes/navbar.html:136
|
||||
msgid "Export and Restore"
|
||||
msgstr "Exportar e Restaurar"
|
||||
|
||||
#: templates/import_app/fragments/profiles/add.html:6
|
||||
msgid "Add new import profile"
|
||||
msgstr "Adicionar novo perfil de importação"
|
||||
@@ -2187,11 +2253,6 @@ msgstr "Uma mensagem do autor"
|
||||
msgid "Edit import profile"
|
||||
msgstr "Editar perfil de importação"
|
||||
|
||||
#: templates/import_app/fragments/profiles/list.html:5
|
||||
#: templates/import_app/pages/profiles_index.html:4
|
||||
msgid "Import Profiles"
|
||||
msgstr "Perfis de Importação"
|
||||
|
||||
#: templates/import_app/fragments/profiles/list.html:17
|
||||
msgid "New"
|
||||
msgstr "Novo"
|
||||
@@ -2318,20 +2379,15 @@ msgstr "Gerenciar"
|
||||
msgid "Automation"
|
||||
msgstr "Automação"
|
||||
|
||||
#: templates/includes/navbar.html:132 templates/rules/fragments/list.html:5
|
||||
#: templates/rules/pages/index.html:4
|
||||
msgid "Rules"
|
||||
msgstr "Regras"
|
||||
|
||||
#: templates/includes/navbar.html:146
|
||||
#: templates/includes/navbar.html:148
|
||||
msgid "Only use this if you know what you're doing"
|
||||
msgstr "Só use isso se você souber o que está fazendo"
|
||||
|
||||
#: templates/includes/navbar.html:147
|
||||
#: templates/includes/navbar.html:149
|
||||
msgid "Django Admin"
|
||||
msgstr "Django Admin"
|
||||
|
||||
#: templates/includes/navbar.html:156
|
||||
#: templates/includes/navbar.html:158
|
||||
msgid "Calculator"
|
||||
msgstr "Calculadora"
|
||||
|
||||
@@ -2364,56 +2420,89 @@ msgstr "Cancelar"
|
||||
msgid "Confirm"
|
||||
msgstr "Confirmar"
|
||||
|
||||
#: templates/insights/fragments/category_explorer/index.html:13
|
||||
#: templates/insights/fragments/category_explorer/charts/account.html:100
|
||||
#: templates/insights/fragments/category_explorer/charts/currency.html:92
|
||||
#: templates/monthly_overview/fragments/monthly_account_summary.html:14
|
||||
#: templates/monthly_overview/fragments/monthly_currency_summary.html:13
|
||||
#: templates/transactions/fragments/all_account_summary.html:14
|
||||
#: templates/transactions/fragments/all_currency_summary.html:13
|
||||
#: templates/transactions/fragments/summary.html:27
|
||||
#: templates/transactions/fragments/summary.html:42
|
||||
#: templates/yearly_overview/fragments/account_data.html:12
|
||||
#: templates/yearly_overview/fragments/currency_data.html:12
|
||||
msgid "No information to display"
|
||||
msgstr "Não há informação para mostrar"
|
||||
|
||||
#: templates/insights/fragments/category_explorer/index.html:14
|
||||
msgid "Income/Expense by Account"
|
||||
msgstr "Gasto/Despesa por Conta"
|
||||
|
||||
#: templates/insights/fragments/category_explorer/index.html:25
|
||||
#: templates/insights/fragments/category_explorer/index.html:26
|
||||
msgid "Income/Expense by Currency"
|
||||
msgstr "Gasto/Despesa por Moeda"
|
||||
|
||||
#: templates/insights/fragments/sankey.html:89
|
||||
#: templates/insights/fragments/late_transactions.html:15
|
||||
msgid "All good!"
|
||||
msgstr "Tudo certo!"
|
||||
|
||||
#: templates/insights/fragments/late_transactions.html:16
|
||||
msgid "No late transactions"
|
||||
msgstr "Nenhuma transação atrasada"
|
||||
|
||||
#: templates/insights/fragments/latest_transactions.html:14
|
||||
msgid "No recent transactions"
|
||||
msgstr "Nenhuma transação recente"
|
||||
|
||||
#: templates/insights/fragments/sankey.html:93
|
||||
msgid "From"
|
||||
msgstr "De"
|
||||
|
||||
#: templates/insights/fragments/sankey.html:92
|
||||
#: templates/insights/fragments/sankey.html:96
|
||||
msgid "Percentage"
|
||||
msgstr "Porcentagem"
|
||||
|
||||
#: templates/insights/pages/index.html:33
|
||||
#: templates/insights/pages/index.html:35
|
||||
msgid "Month"
|
||||
msgstr "Mês"
|
||||
|
||||
#: templates/insights/pages/index.html:36
|
||||
#: templates/insights/pages/index.html:38
|
||||
#: templates/yearly_overview/pages/overview_by_account.html:61
|
||||
#: templates/yearly_overview/pages/overview_by_currency.html:63
|
||||
msgid "Year"
|
||||
msgstr "Ano"
|
||||
|
||||
#: templates/insights/pages/index.html:39
|
||||
#: templates/insights/pages/index.html:43
|
||||
msgid "Month Range"
|
||||
msgstr "Intervalo de Mês"
|
||||
|
||||
#: templates/insights/pages/index.html:42
|
||||
#: templates/insights/pages/index.html:48
|
||||
msgid "Year Range"
|
||||
msgstr "Intervalo de Ano"
|
||||
|
||||
#: templates/insights/pages/index.html:45
|
||||
#: templates/insights/pages/index.html:53
|
||||
msgid "Date Range"
|
||||
msgstr "Intervalo de Data"
|
||||
|
||||
#: templates/insights/pages/index.html:74
|
||||
#: templates/insights/pages/index.html:81
|
||||
msgid "Account Flow"
|
||||
msgstr "Fluxo de Conta"
|
||||
|
||||
#: templates/insights/pages/index.html:81
|
||||
#: templates/insights/pages/index.html:88
|
||||
msgid "Currency Flow"
|
||||
msgstr "Fluxo de Moeda"
|
||||
|
||||
#: templates/insights/pages/index.html:88
|
||||
#: templates/insights/pages/index.html:95
|
||||
msgid "Category Explorer"
|
||||
msgstr "Explorador de Categoria"
|
||||
|
||||
#: templates/insights/pages/index.html:102
|
||||
msgid "Late Transactions"
|
||||
msgstr "Transações Atrasadas"
|
||||
|
||||
#: templates/insights/pages/index.html:108
|
||||
msgid "Latest Transactions"
|
||||
msgstr "Últimas Transações"
|
||||
|
||||
#: templates/installment_plans/fragments/add.html:5
|
||||
msgid "Add installment plan"
|
||||
msgstr "Adicionar parcelamento"
|
||||
@@ -2483,17 +2572,6 @@ msgstr "Item"
|
||||
msgid "No transactions this month"
|
||||
msgstr "Nenhuma transação neste mês"
|
||||
|
||||
#: templates/monthly_overview/fragments/monthly_account_summary.html:14
|
||||
#: templates/monthly_overview/fragments/monthly_currency_summary.html:13
|
||||
#: templates/transactions/fragments/all_account_summary.html:14
|
||||
#: templates/transactions/fragments/all_currency_summary.html:13
|
||||
#: templates/transactions/fragments/summary.html:27
|
||||
#: templates/transactions/fragments/summary.html:42
|
||||
#: templates/yearly_overview/fragments/account_data.html:12
|
||||
#: templates/yearly_overview/fragments/currency_data.html:12
|
||||
msgid "No information to display"
|
||||
msgstr "Não há informação para mostrar"
|
||||
|
||||
#: templates/monthly_overview/fragments/monthly_summary.html:6
|
||||
msgid "Daily Spending Allowance"
|
||||
msgstr "Gasto Diário"
|
||||
@@ -2514,6 +2592,10 @@ msgstr "atual"
|
||||
msgid "projected"
|
||||
msgstr "previsto"
|
||||
|
||||
#: templates/monthly_overview/fragments/monthly_summary.html:103
|
||||
msgid "Expenses"
|
||||
msgstr "Despesas"
|
||||
|
||||
#: templates/monthly_overview/fragments/monthly_summary.html:167
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
@@ -2780,6 +2862,31 @@ msgstr "Mostrar valores"
|
||||
msgid "Yearly Overview"
|
||||
msgstr "Visão Anual"
|
||||
|
||||
#, fuzzy
|
||||
#~| msgid "From Amount"
|
||||
#~ msgid "Principal Amount"
|
||||
#~ msgstr "Quantia de origem"
|
||||
|
||||
#, fuzzy
|
||||
#~| msgid "Interval"
|
||||
#~ msgid "Interest"
|
||||
#~ msgstr "Intervalo"
|
||||
|
||||
#, fuzzy
|
||||
#~| msgid "Management"
|
||||
#~ msgid "Loan Payment"
|
||||
#~ msgstr "Gerenciar"
|
||||
|
||||
#, fuzzy
|
||||
#~| msgid "Management"
|
||||
#~ msgid "Loan Payments"
|
||||
#~ msgstr "Gerenciar"
|
||||
|
||||
#, fuzzy
|
||||
#~| msgid "Installment Plans"
|
||||
#~ msgid "Installment Planss"
|
||||
#~ msgstr "Parcelamentos"
|
||||
|
||||
#, fuzzy
|
||||
#~| msgid "Tags"
|
||||
#~ msgid "No Tags"
|
||||
@@ -2840,11 +2947,6 @@ msgstr "Visão Anual"
|
||||
#~ msgid "Reference Date Operator"
|
||||
#~ msgstr "Data de Referência de"
|
||||
|
||||
#, fuzzy
|
||||
#~| msgid "From Amount"
|
||||
#~ msgid "Search Amount"
|
||||
#~ msgstr "Quantia de origem"
|
||||
|
||||
#, fuzzy
|
||||
#~| msgid "Amount max"
|
||||
#~ msgid "Amount Operator"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<div class="row {% if not remove_padding %}p-5{% endif %}">
|
||||
<div class="col {% if not remove_padding %}p-5{% endif %}">
|
||||
<div class="text-center">
|
||||
<i class="fa-solid fa-circle-xmark tw-text-6xl"></i>
|
||||
<i class="{% if icon %}{{ icon }}{% else %}fa-solid fa-circle-xmark{% endif %} tw-text-6xl"></i>
|
||||
<p class="lead mt-4 mb-0">{{ title }}</p>
|
||||
<p class="tw-text-gray-500">{{ subtitle }}</p>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% load markdown %}
|
||||
{% load i18n %}
|
||||
<div class="transaction">
|
||||
<div class="d-flex my-1 {% if transaction.type == "EX" %}expense{% else %}income{% endif %}">
|
||||
<div class="transaction {% if transaction.type == "EX" %}expense{% else %}income{% endif %}">
|
||||
<div class="d-flex my-1">
|
||||
{% if not disable_selection %}
|
||||
<label class="px-3 d-flex align-items-center justify-content-center">
|
||||
<input class="form-check-input" type="checkbox" name="transactions" value="{{ transaction.id }}"
|
||||
@@ -15,9 +15,9 @@
|
||||
_="on mouseover remove .tw-invisible from the first .transaction-actions in me end
|
||||
on mouseout add .tw-invisible to the first .transaction-actions in me end">
|
||||
<div class="row font-monospace tw-text-sm align-items-center">
|
||||
<div class="col-lg-1 col-12 d-flex align-items-center tw-text-2xl lg:tw-text-xl text-lg-center text-center">
|
||||
<div class="col-lg-auto col-12 d-flex align-items-center tw-text-2xl lg:tw-text-xl text-lg-center text-center p-0 ps-1">
|
||||
{% if not transaction.deleted %}
|
||||
<a class="text-decoration-none my-lg-3 mx-lg-3 mx-2 my-2 tw-text-gray-500"
|
||||
<a class="text-decoration-none p-3 tw-text-gray-500"
|
||||
title="{% if transaction.is_paid %}{% trans 'Paid' %}{% else %}{% trans 'Projected' %}{% endif %}"
|
||||
role="button"
|
||||
hx-get="{% url 'transaction_pay' transaction_id=transaction.id %}"
|
||||
@@ -27,14 +27,14 @@
|
||||
class="fa-regular fa-circle"></i>{% endif %}
|
||||
</a>
|
||||
{% else %}
|
||||
<div class="text-decoration-none my-lg-3 mx-lg-3 mx-2 my-2 tw-text-gray-500"
|
||||
<div class="text-decoration-none p-3 tw-text-gray-500"
|
||||
title="{% if transaction.is_paid %}{% trans 'Paid' %}{% else %}{% trans 'Projected' %}{% endif %}">
|
||||
{% if transaction.is_paid %}<i class="fa-regular fa-circle-check"></i>{% else %}<i
|
||||
class="fa-regular fa-circle"></i>{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-lg-8 col-12">
|
||||
<div class="col-lg col-12">
|
||||
{# Date#}
|
||||
<div class="row mb-2 mb-lg-1 tw-text-gray-400">
|
||||
<div class="col-auto pe-1"><i class="fa-solid fa-calendar fa-fw me-1 fa-xs"></i></div>
|
||||
@@ -92,7 +92,7 @@
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3 col-12 text-lg-end align-self-end">
|
||||
<div class="col-lg-auto col-12 text-lg-end align-self-end">
|
||||
<div class="main-amount mb-2 mb-lg-0">
|
||||
<c-amount.display
|
||||
:amount="transaction.amount"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<div class="card tw-relative h-100 shadow">
|
||||
<div class="tw-absolute tw-h-8 tw-w-8 tw-right-2 tw-top-2 tw-bg-{{ color }}-300 tw-text-{{ color }}-800 text-center align-items-center d-flex justify-content-center rounded-2">
|
||||
<i class="{{ icon }}"></i>
|
||||
{% if icon %}<i class="{{ icon }}"></i>{% else %}<span class="fw-bold">{{ title.0 }}</span>{% endif %}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="tw-text-{{ color }}-400 fw-bold tw-mr-[50px]" {{ attrs }}>{{ title }}{% if help_text %}{% include 'includes/help_icon.html' with content=help_text %}{% endif %}</h5>
|
||||
{{ slot }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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>
|
||||
|
||||
13
app/templates/export_app/fragments/export.html
Normal file
13
app/templates/export_app/fragments/export.html
Normal 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 %}
|
||||
17
app/templates/export_app/fragments/restore.html
Normal file
17
app/templates/export_app/fragments/restore.html
Normal 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 %}
|
||||
29
app/templates/export_app/pages/index.html
Normal file
29
app/templates/export_app/pages/index.html
Normal 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 %}
|
||||
@@ -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>
|
||||
|
||||
@@ -1,79 +1,101 @@
|
||||
{% load i18n %}
|
||||
<div class="chart-container" style="position: relative; height:400px; width:100%" _="init call setupAccountChart() end">
|
||||
<canvas id="accountChart"></canvas>
|
||||
</div>
|
||||
{% if account_data.labels %}
|
||||
<div class="chart-container" style="position: relative; height:400px; width:100%"
|
||||
_="init call setupAccountChart() end">
|
||||
<canvas id="accountChart"></canvas>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Get the data from your Django view (passed as JSON)
|
||||
var accountData = {{ account_data|safe }};
|
||||
<script>
|
||||
// Get the data from your Django view (passed as JSON)
|
||||
var accountData = {{ account_data|safe }};
|
||||
|
||||
function setupAccountChart() {
|
||||
var chartOptions = {
|
||||
indexAxis: 'y', // This makes the chart horizontal
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true, // Enable stacking on the x-axis
|
||||
title: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
y: {
|
||||
stacked: true, // Enable stacking on the y-axis
|
||||
title: {
|
||||
display: false,
|
||||
},
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function (context) {
|
||||
if (context.parsed.x !== null) {
|
||||
return new Intl.NumberFormat(undefined).format(Math.abs(context.parsed.x)); // Using abs for display
|
||||
}
|
||||
return "";
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
function setupAccountChart() {
|
||||
var chartOptions = {
|
||||
indexAxis: 'y', // This makes the chart horizontal
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true, // Enable stacking on the x-axis
|
||||
title: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
y: {
|
||||
stacked: true, // Enable stacking on the y-axis
|
||||
title: {
|
||||
display: false,
|
||||
},
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function (context) {
|
||||
if (context.parsed.x !== null) {
|
||||
return `${context.dataset.label}: ${new Intl.NumberFormat(undefined, {
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 30,
|
||||
roundingMode: 'trunc'
|
||||
}).format(Math.abs(context.parsed.x))}`;
|
||||
}
|
||||
return "";
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
new Chart(
|
||||
document.getElementById('accountChart'),
|
||||
{
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: accountData.labels,
|
||||
datasets: [
|
||||
{
|
||||
label: "{% trans 'Income' %}",
|
||||
data: accountData.datasets[0].data,
|
||||
backgroundColor: '#4dde80',
|
||||
stack: 'stack0'
|
||||
},
|
||||
{
|
||||
label: "{% trans 'Expenses' %}",
|
||||
data: accountData.datasets[1].data,
|
||||
backgroundColor: '#f87171',
|
||||
stack: 'stack0'
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
...chartOptions,
|
||||
plugins: {
|
||||
...chartOptions.plugins,
|
||||
title: {
|
||||
display: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
</script>
|
||||
new Chart(
|
||||
document.getElementById('accountChart'),
|
||||
{
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: accountData.labels,
|
||||
datasets: [
|
||||
{
|
||||
label: "{% trans 'Projected Expenses' %}",
|
||||
data: accountData.datasets[3].data,
|
||||
backgroundColor: '#f8717180', // Added transparency
|
||||
stack: 'stack0'
|
||||
},
|
||||
{
|
||||
label: "{% trans 'Current Expenses' %}",
|
||||
data: accountData.datasets[1].data,
|
||||
backgroundColor: '#f87171',
|
||||
stack: 'stack0'
|
||||
},
|
||||
{
|
||||
label: "{% trans 'Current Income' %}",
|
||||
data: accountData.datasets[0].data,
|
||||
backgroundColor: '#4dde80',
|
||||
stack: 'stack0'
|
||||
},
|
||||
{
|
||||
label: "{% trans 'Projected Income' %}",
|
||||
data: accountData.datasets[2].data,
|
||||
backgroundColor: '#4dde8080', // Added transparency
|
||||
stack: 'stack0'
|
||||
},
|
||||
|
||||
]
|
||||
},
|
||||
options: {
|
||||
...chartOptions,
|
||||
plugins: {
|
||||
...chartOptions.plugins,
|
||||
title: {
|
||||
display: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
</script>
|
||||
{% else %}
|
||||
<c-msg.empty title="{% translate "No information to display" %}"></c-msg.empty>
|
||||
{% endif %}
|
||||
|
||||
@@ -1,80 +1,93 @@
|
||||
{% load i18n %}
|
||||
<div class="chart-container" style="position: relative; height:400px; width:100%"
|
||||
_="init call setupCurrencyChart() end">
|
||||
<canvas id="currencyChart"></canvas>
|
||||
</div>
|
||||
{% if currency_data.labels %}
|
||||
<div class="chart-container" style="position: relative; height:400px; width:100%"
|
||||
_="init call setupCurrencyChart() end">
|
||||
<canvas id="currencyChart"></canvas>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Get the data from your Django view (passed as JSON)
|
||||
var currencyData = {{ currency_data|safe }};
|
||||
<script>
|
||||
// Get the data from your Django view (passed as JSON)
|
||||
var currencyData = {{ currency_data|safe }};
|
||||
|
||||
function setupCurrencyChart() {
|
||||
var chartOptions = {
|
||||
indexAxis: 'y', // This makes the chart horizontal
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true, // Enable stacking on the x-axis
|
||||
title: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
y: {
|
||||
stacked: true, // Enable stacking on the y-axis
|
||||
title: {
|
||||
display: false,
|
||||
},
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function (context) {
|
||||
if (context.parsed.x !== null) {
|
||||
return new Intl.NumberFormat(undefined).format(Math.abs(context.parsed.x)); // Using abs for display
|
||||
}
|
||||
return "";
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
function setupCurrencyChart() {
|
||||
var chartOptions = {
|
||||
indexAxis: 'y',
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true,
|
||||
title: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
title: {
|
||||
display: false,
|
||||
},
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function (context) {
|
||||
if (context.parsed.x !== null) {
|
||||
return `${context.dataset.label}: ${new Intl.NumberFormat(undefined, {
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 30,
|
||||
roundingMode: 'trunc'
|
||||
}).format(Math.abs(context.parsed.x))}`;
|
||||
}
|
||||
return "";
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
new Chart(
|
||||
document.getElementById('currencyChart'),
|
||||
{
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: currencyData.labels,
|
||||
datasets: [
|
||||
{
|
||||
label: "{% trans 'Income' %}",
|
||||
data: currencyData.datasets[0].data,
|
||||
backgroundColor: '#4dde80',
|
||||
stack: 'stack0'
|
||||
},
|
||||
{
|
||||
label: "{% trans 'Expenses' %}",
|
||||
data: currencyData.datasets[1].data,
|
||||
backgroundColor: '#f87171',
|
||||
stack: 'stack0'
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
...chartOptions,
|
||||
plugins: {
|
||||
...chartOptions.plugins,
|
||||
title: {
|
||||
display: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
</script>
|
||||
new Chart(
|
||||
document.getElementById('currencyChart'),
|
||||
{
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: currencyData.labels,
|
||||
datasets: [
|
||||
{
|
||||
label: "{% trans 'Projected Expenses' %}",
|
||||
data: currencyData.datasets[3].data,
|
||||
backgroundColor: '#f8717180', // Added transparency
|
||||
stack: 'stack0'
|
||||
},
|
||||
{
|
||||
label: "{% trans 'Current Expenses' %}",
|
||||
data: currencyData.datasets[1].data,
|
||||
backgroundColor: '#f87171',
|
||||
stack: 'stack0'
|
||||
},
|
||||
{
|
||||
label: "{% trans 'Current Income' %}",
|
||||
data: currencyData.datasets[0].data,
|
||||
backgroundColor: '#4dde80',
|
||||
stack: 'stack0'
|
||||
},
|
||||
{
|
||||
label: "{% trans 'Projected Income' %}",
|
||||
data: currencyData.datasets[2].data,
|
||||
backgroundColor: '#4dde8080', // Added transparency
|
||||
stack: 'stack0'
|
||||
},
|
||||
|
||||
]
|
||||
},
|
||||
options: chartOptions
|
||||
}
|
||||
);
|
||||
}
|
||||
</script>
|
||||
{% else %}
|
||||
<c-msg.empty title="{% translate "No information to display" %}"></c-msg.empty>
|
||||
{% endif %}
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
<form _="install init_tom_select
|
||||
on change trigger updated" id="category-form">
|
||||
on change trigger updated
|
||||
init trigger updated" id="category-form">
|
||||
{% crispy category_form %}
|
||||
</form>
|
||||
|
||||
<div class="row row-cols-1 row-cols-lg-2 gx-3 gy-3">
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<div class="card h-100">
|
||||
<div class="card-header">
|
||||
{% trans "Income/Expense by Account" %}
|
||||
</div>
|
||||
@@ -20,14 +21,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<div class="card h-100">
|
||||
<div class="card-header">
|
||||
{% trans "Income/Expense by Currency" %}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="currency-card" class="show-loading" hx-get="{% url 'category_sum_by_currency' %}"
|
||||
hx-trigger="updated from:window" hx-include="#category-form, #picker-form, #picker-type">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
18
app/templates/insights/fragments/late_transactions.html
Normal file
18
app/templates/insights/fragments/late_transactions.html
Normal file
@@ -0,0 +1,18 @@
|
||||
{% load i18n %}
|
||||
|
||||
|
||||
<div hx-get="{% url 'insights_late_transactions' %}" hx-trigger="updated from:window" class="show-loading"
|
||||
id="transactions-list" hx-swap="outerHTML">
|
||||
{% if transactions %}
|
||||
{% for transaction in transactions %}
|
||||
<c-transaction.item :transaction="transaction"></c-transaction.item>
|
||||
{% endfor %}
|
||||
{# Floating bar #}
|
||||
<c-ui.transactions-action-bar></c-ui.transactions-action-bar>
|
||||
{% else %}
|
||||
<c-msg.empty
|
||||
icon="fa-regular fa-hourglass"
|
||||
title="{% translate 'All good!' %}"
|
||||
subtitle="{% translate "No late transactions" %}"></c-msg.empty>
|
||||
{% endif %}
|
||||
</div>
|
||||
16
app/templates/insights/fragments/latest_transactions.html
Normal file
16
app/templates/insights/fragments/latest_transactions.html
Normal file
@@ -0,0 +1,16 @@
|
||||
{% load i18n %}
|
||||
|
||||
|
||||
<div hx-get="{% url 'insights_late_transactions' %}" hx-trigger="updated from:window" class="show-loading"
|
||||
id="transactions-list" hx-swap="outerHTML">
|
||||
{% if transactions %}
|
||||
{% for transaction in transactions %}
|
||||
<c-transaction.item :transaction="transaction"></c-transaction.item>
|
||||
{% endfor %}
|
||||
{# Floating bar #}
|
||||
<c-ui.transactions-action-bar></c-ui.transactions-action-bar>
|
||||
{% else %}
|
||||
<c-msg.empty
|
||||
title="{% translate 'No recent transactions' %}"></c-msg.empty>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -7,7 +7,7 @@
|
||||
<div class="show-loading" hx-get="{% url 'insights_sankey_by_currency' %}" hx-trigger="updated from:window"
|
||||
hx-swap="outerHTML" hx-include="#picker-form, #picker-type">
|
||||
{% endif %}
|
||||
<div class="chart-container position-relative tw-min-h-[60vh] tw-max-h-[60vh] tw-h-full tw-w-full"
|
||||
<div class="chart-container position-relative tw-min-h-[85vh] tw-max-h-[85vh] tw-h-full tw-w-full"
|
||||
id="sankeyContainer"
|
||||
_="init call setupSankeyChart() end">
|
||||
<canvas id="sankeyChart"></canvas>
|
||||
@@ -64,6 +64,7 @@
|
||||
alpha: 0.5,
|
||||
size: 'max',
|
||||
color: "white",
|
||||
nodePadding: 30,
|
||||
priority: data.nodes.reduce((acc, node) => {
|
||||
acc[node.id] = node.priority;
|
||||
return acc;
|
||||
@@ -77,6 +78,9 @@
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
layout: {
|
||||
padding: 20
|
||||
},
|
||||
plugins: {
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row my-3">
|
||||
<div class="row my-3 h-100">
|
||||
<div class="col-lg-2 col-md-3 mb-3 mb-md-0">
|
||||
<div class="">
|
||||
<div class="mb-2 w-100 d-lg-inline-flex d-grid gap-2 flex-wrap justify-content-lg-center" role="group"
|
||||
_="on change
|
||||
<div class="position-sticky tw-top-3">
|
||||
<div class="">
|
||||
<div class="mb-2 w-100 d-lg-inline-flex d-grid gap-2 flex-wrap justify-content-lg-center" role="group"
|
||||
_="on change
|
||||
set type to event.target.value
|
||||
add .tw-hidden to <#picker-form > div:not(.tw-hidden)/>
|
||||
|
||||
@@ -28,65 +29,85 @@
|
||||
remove .tw-hidden from #date-range-form
|
||||
end
|
||||
then trigger updated"
|
||||
id="picker-type">
|
||||
<input type="radio" class="btn-check" name="type" value="month" id="monthradio" autocomplete="off" checked>
|
||||
<label class="btn btn-sm btn-outline-primary flex-grow-1" for="monthradio">{% translate 'Month' %}</label>
|
||||
id="picker-type">
|
||||
<input type="radio" class="btn-check" name="type" value="month" id="monthradio" autocomplete="off"
|
||||
checked>
|
||||
<label class="btn btn-sm btn-outline-primary flex-grow-1" for="monthradio">{% translate 'Month' %}</label>
|
||||
|
||||
<input type="radio" class="btn-check" name="type" value="year" id="yearradio" autocomplete="off">
|
||||
<label class="btn btn-sm btn-outline-primary flex-grow-1" for="yearradio">{% translate 'Year' %}</label>
|
||||
<input type="radio" class="btn-check" name="type" value="year" id="yearradio" autocomplete="off">
|
||||
<label class="btn btn-sm btn-outline-primary flex-grow-1" for="yearradio">{% translate 'Year' %}</label>
|
||||
|
||||
<input type="radio" class="btn-check" name="type" value="month-range" id="monthrangeradio" autocomplete="off">
|
||||
<label class="btn btn-sm btn-outline-primary flex-grow-1" for="monthrangeradio">{% translate 'Month Range' %}</label>
|
||||
<input type="radio" class="btn-check" name="type" value="month-range" id="monthrangeradio"
|
||||
autocomplete="off">
|
||||
<label class="btn btn-sm btn-outline-primary flex-grow-1"
|
||||
for="monthrangeradio">{% translate 'Month Range' %}</label>
|
||||
|
||||
<input type="radio" class="btn-check" name="type" value="year-range" id="yearrangeradio" autocomplete="off">
|
||||
<label class="btn btn-sm btn-outline-primary flex-grow-1" for="yearrangeradio">{% translate 'Year Range' %}</label>
|
||||
<input type="radio" class="btn-check" name="type" value="year-range" id="yearrangeradio"
|
||||
autocomplete="off">
|
||||
<label class="btn btn-sm btn-outline-primary flex-grow-1"
|
||||
for="yearrangeradio">{% translate 'Year Range' %}</label>
|
||||
|
||||
<input type="radio" class="btn-check" name="type" value="date-range" id="daterangeradio" autocomplete="off">
|
||||
<label class="btn btn-sm btn-outline-primary flex-grow-1" for="daterangeradio">{% translate 'Date Range' %}</label>
|
||||
</div>
|
||||
<form id="picker-form"
|
||||
_="install init_datepicker
|
||||
<input type="radio" class="btn-check" name="type" value="date-range" id="daterangeradio"
|
||||
autocomplete="off">
|
||||
<label class="btn btn-sm btn-outline-primary flex-grow-1"
|
||||
for="daterangeradio">{% translate 'Date Range' %}</label>
|
||||
</div>
|
||||
<form id="picker-form"
|
||||
_="install init_datepicker
|
||||
on change trigger updated">
|
||||
<div id="month-form" class="">
|
||||
{% crispy month_form %}
|
||||
</div>
|
||||
<div id="year-form" class="tw-hidden">
|
||||
{% crispy year_form %}
|
||||
</div>
|
||||
<div id="month-range-form" class="tw-hidden">
|
||||
{% crispy month_range_form %}
|
||||
</div>
|
||||
<div id="year-range-form" class="tw-hidden">
|
||||
{% crispy year_range_form %}
|
||||
</div>
|
||||
<div id="date-range-form" class="tw-hidden">
|
||||
{% crispy date_range_form %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<hr class="mt-0">
|
||||
<div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist"
|
||||
aria-orientation="vertical">
|
||||
<button class="nav-link" id="v-pills-tab" data-bs-toggle="pill" data-bs-target="#v-pills-content"
|
||||
type="button" role="tab" aria-controls="v-pills-content" aria-selected="false"
|
||||
hx-get="{% url 'insights_sankey_by_account' %}" hx-include="#picker-form, #picker-type"
|
||||
hx-indicator="#tab-content"
|
||||
hx-target="#tab-content">{% trans 'Account Flow' %}
|
||||
</button>
|
||||
<button class="nav-link" id="v-pills-tab" data-bs-toggle="pill" data-bs-target="#v-pills-content"
|
||||
type="button" role="tab" aria-controls="v-pills-content" aria-selected="false"
|
||||
hx-get="{% url 'insights_sankey_by_currency' %}"
|
||||
hx-include="#picker-form, #picker-type"
|
||||
hx-indicator="#tab-content"
|
||||
hx-target="#tab-content">{% trans 'Currency Flow' %}
|
||||
</button>
|
||||
<button class="nav-link" id="v-pills-tab" data-bs-toggle="pill" data-bs-target="#v-pills-content"
|
||||
type="button" role="tab" aria-controls="v-pills-content" aria-selected="false"
|
||||
hx-get="{% url 'category_explorer_index' %}"
|
||||
hx-include="#picker-form, #picker-type"
|
||||
hx-indicator="#tab-content"
|
||||
hx-target="#tab-content">{% trans 'Category Explorer' %}
|
||||
</button>
|
||||
<div id="month-form" class="">
|
||||
{% crispy month_form %}
|
||||
</div>
|
||||
<div id="year-form" class="tw-hidden">
|
||||
{% crispy year_form %}
|
||||
</div>
|
||||
<div id="month-range-form" class="tw-hidden">
|
||||
{% crispy month_range_form %}
|
||||
</div>
|
||||
<div id="year-range-form" class="tw-hidden">
|
||||
{% crispy year_range_form %}
|
||||
</div>
|
||||
<div id="date-range-form" class="tw-hidden">
|
||||
{% crispy date_range_form %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist"
|
||||
aria-orientation="vertical">
|
||||
<button class="nav-link" id="v-pills-tab" data-bs-toggle="pill" data-bs-target="#v-pills-content"
|
||||
type="button" role="tab" aria-controls="v-pills-content" aria-selected="false"
|
||||
hx-get="{% url 'insights_sankey_by_account' %}" hx-include="#picker-form, #picker-type"
|
||||
hx-indicator="#tab-content"
|
||||
hx-target="#tab-content">{% trans 'Account Flow' %}
|
||||
</button>
|
||||
<button class="nav-link" id="v-pills-tab" data-bs-toggle="pill" data-bs-target="#v-pills-content"
|
||||
type="button" role="tab" aria-controls="v-pills-content" aria-selected="false"
|
||||
hx-get="{% url 'insights_sankey_by_currency' %}"
|
||||
hx-include="#picker-form, #picker-type"
|
||||
hx-indicator="#tab-content"
|
||||
hx-target="#tab-content">{% trans 'Currency Flow' %}
|
||||
</button>
|
||||
<button class="nav-link" id="v-pills-tab" data-bs-toggle="pill" data-bs-target="#v-pills-content"
|
||||
type="button" role="tab" aria-controls="v-pills-content" aria-selected="false"
|
||||
hx-get="{% url 'category_explorer_index' %}"
|
||||
hx-include="#picker-form, #picker-type"
|
||||
hx-indicator="#tab-content"
|
||||
hx-target="#tab-content">{% trans 'Category Explorer' %}
|
||||
</button>
|
||||
<hr>
|
||||
<button class="nav-link" id="v-pills-tab" data-bs-toggle="pill" data-bs-target="#v-pills-content"
|
||||
type="button" role="tab" aria-controls="v-pills-content" aria-selected="false"
|
||||
hx-get="{% url 'insights_late_transactions' %}"
|
||||
hx-indicator="#tab-content"
|
||||
hx-target="#tab-content">{% trans 'Late Transactions' %}
|
||||
</button>
|
||||
<button class="nav-link" id="v-pills-tab" data-bs-toggle="pill" data-bs-target="#v-pills-content"
|
||||
type="button" role="tab" aria-controls="v-pills-content" aria-selected="false"
|
||||
hx-get="{% url 'insights_latest_transactions' %}"
|
||||
hx-indicator="#tab-content"
|
||||
hx-target="#tab-content">{% trans 'Latest Transactions' %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-9 col-lg-10">
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="tw-cursor-pointer text-primary text-end"
|
||||
<div class="tw-cursor-pointer text-primary text-end"
|
||||
_="on click
|
||||
set from_value to #id_from_currency's value
|
||||
set to_value to #id_to_currency's value
|
||||
@@ -58,5 +58,39 @@
|
||||
<i class="fa-solid fa-rotate me-2"></i><span>{% trans 'Invert' %}</span>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
|
||||
{% for currency, data in rate_map.items %}
|
||||
<div class="col">
|
||||
<c-ui.info-card color="yellow" title="{{ currency }}">
|
||||
{% for rate in data.rates.values %}
|
||||
<div class="d-flex justify-content-between align-items-baseline mt-2">
|
||||
<div class="text-end font-monospace">
|
||||
<div class="tw-text-gray-400">
|
||||
{# <c-amount.display#}
|
||||
{# :amount="1"#}
|
||||
{# :prefix="data.prefix"#}
|
||||
{# :suffix="data.suffix"#}
|
||||
{# :decimal_places="data.decimal_places"></c-amount.display>#}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dotted-line flex-grow-1"></div>
|
||||
{% if currency.income_projected != 0 %}
|
||||
<div class="text-end font-monospace tw-text-green-400">
|
||||
<c-amount.display
|
||||
:amount="rate.rate"
|
||||
:prefix="rate.prefix"
|
||||
:suffix="rate.suffix"
|
||||
:decimal_places="rate.decimal_places"></c-amount.display>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-end font-monospace">-</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</c-ui.info-card>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -174,7 +174,7 @@
|
||||
</div>
|
||||
<div id="search" class="my-3">
|
||||
<label class="w-100">
|
||||
<input type="search" class="form-control" placeholder="Buscar" hx-preserve id="quick-search"
|
||||
<input type="search" class="form-control" placeholder="{% translate 'Search' %}" hx-preserve id="quick-search"
|
||||
_="on input or search or htmx:afterSwap from window
|
||||
if my value is empty
|
||||
trigger toggle on <.transactions-divider-collapse/>
|
||||
|
||||
@@ -2,12 +2,14 @@ import AirDatepicker from 'air-datepicker';
|
||||
import en from 'air-datepicker/locale/en';
|
||||
import ptBr from 'air-datepicker/locale/pt-BR';
|
||||
import nl from 'air-datepicker/locale/nl';
|
||||
import de from 'air-datepicker/locale/de';
|
||||
import {createPopper} from '@popperjs/core';
|
||||
|
||||
const locales = {
|
||||
'pt': ptBr,
|
||||
'en': en,
|
||||
'nl': nl
|
||||
'nl': nl,
|
||||
'de': de
|
||||
};
|
||||
|
||||
function isMobileDevice() {
|
||||
@@ -40,9 +42,10 @@ window.DatePicker = function createDynamicDatePicker(element) {
|
||||
dateFormat: element.dataset.dateFormat,
|
||||
timeFormat: element.dataset.timeFormat,
|
||||
timepicker: element.dataset.timepicker === 'true',
|
||||
toggleSelected: element.dataset.toggleSelected === 'true',
|
||||
autoClose: element.dataset.autoClose === 'true',
|
||||
buttons: element.dataset.clearButton === 'true' ? ['clear', todayButton] : [todayButton],
|
||||
locale: locales[element.dataset.language],
|
||||
locale: locales[element.dataset.language] || locales['en'],
|
||||
onSelect: ({date, formattedDate, datepicker}) => {
|
||||
const _event = new CustomEvent("change", {
|
||||
bubbles: true,
|
||||
@@ -96,7 +99,6 @@ window.DatePicker = function createDynamicDatePicker(element) {
|
||||
return new AirDatepicker(element, opts);
|
||||
};
|
||||
|
||||
|
||||
window.MonthYearPicker = function createDynamicDatePicker(element) {
|
||||
let todayButton = {
|
||||
content: element.dataset.nowButtonTxt,
|
||||
@@ -114,9 +116,10 @@ window.MonthYearPicker = function createDynamicDatePicker(element) {
|
||||
view: 'months',
|
||||
minView: 'months',
|
||||
dateFormat: 'MMMM yyyy',
|
||||
toggleSelected: element.dataset.toggleSelected === 'true',
|
||||
autoClose: element.dataset.autoClose === 'true',
|
||||
buttons: element.dataset.clearButton === 'true' ? ['clear', todayButton] : [todayButton],
|
||||
locale: locales[element.dataset.language],
|
||||
locale: locales[element.dataset.language] || locales['en'],
|
||||
onSelect: ({date, formattedDate, datepicker}) => {
|
||||
const _event = new CustomEvent("change", {
|
||||
bubbles: true,
|
||||
@@ -163,8 +166,8 @@ window.MonthYearPicker = function createDynamicDatePicker(element) {
|
||||
let opts = {...baseOpts, ...positionConfig};
|
||||
|
||||
if (element.dataset.value) {
|
||||
opts["selectedDates"] = [new Date(element.dataset.value + "T00:00:00")];
|
||||
opts["startDate"] = [new Date(element.dataset.value + "T00:00:00")];
|
||||
opts["selectedDates"] = [new Date(element.dataset.value + "T00:00:00")];
|
||||
opts["startDate"] = [new Date(element.dataset.value + "T00:00:00")];
|
||||
}
|
||||
return new AirDatepicker(element, opts);
|
||||
};
|
||||
@@ -186,9 +189,10 @@ window.YearPicker = function createDynamicDatePicker(element) {
|
||||
view: 'years',
|
||||
minView: 'years',
|
||||
dateFormat: 'yyyy',
|
||||
toggleSelected: element.dataset.toggleSelected === 'true',
|
||||
autoClose: element.dataset.autoClose === 'true',
|
||||
buttons: element.dataset.clearButton === 'true' ? ['clear', todayButton] : [todayButton],
|
||||
locale: locales[element.dataset.language],
|
||||
locale: locales[element.dataset.language] || locales['en'],
|
||||
onSelect: ({date, formattedDate, datepicker}) => {
|
||||
const _event = new CustomEvent("change", {
|
||||
bubbles: true,
|
||||
@@ -235,8 +239,8 @@ window.YearPicker = function createDynamicDatePicker(element) {
|
||||
let opts = {...baseOpts, ...positionConfig};
|
||||
|
||||
if (element.dataset.value) {
|
||||
opts["selectedDates"] = [new Date(element.dataset.value + "T00:00:00")];
|
||||
opts["startDate"] = [new Date(element.dataset.value + "T00:00:00")];
|
||||
opts["selectedDates"] = [new Date(element.dataset.value + "T00:00:00")];
|
||||
opts["startDate"] = [new Date(element.dataset.value + "T00:00:00")];
|
||||
}
|
||||
return new AirDatepicker(element, opts);
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user