mirror of
https://github.com/eitchtee/WYGIWYH.git
synced 2026-04-28 19:47:14 +02:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aba47f0eed | ||
|
|
2010ccc92d | ||
|
|
d73d6cbf22 | ||
|
|
e5a9b6e921 | ||
|
|
dbd9774681 | ||
|
|
5a93a907e1 | ||
|
|
e0e159166b | ||
|
|
6c7594ad14 | ||
|
|
d3ea0e43da | ||
|
|
dde75416ca | ||
|
|
c9b346b791 | ||
|
|
9896044a15 | ||
|
|
eb65eb4590 | ||
|
|
017c70e8b2 | ||
|
|
64b0830909 | ||
|
|
25d99cbece | ||
|
|
033f0e1b0d | ||
|
|
35027ee0ae | ||
|
|
91904e959b | ||
|
|
a6a85ae3a2 | ||
|
|
b0f53f45f9 | ||
|
|
0f60f8d486 | ||
|
|
efb207a109 | ||
|
|
95b1481dd5 | ||
|
|
8de340b68b | ||
|
|
ef15b85386 | ||
|
|
45d939237d | ||
|
|
6bf262e514 | ||
|
|
f9d9137336 | ||
|
|
b532521f27 | ||
|
|
1e06e2d34d | ||
|
|
a33fa5e184 | ||
|
|
a2453695d8 | ||
|
|
3e929d0433 | ||
|
|
185fc464a5 | ||
|
|
647c009525 | ||
|
|
ba75492dcc | ||
|
|
8312baaf45 | ||
|
|
4d346dc278 | ||
|
|
70ff7fab38 | ||
|
|
6947c6affd | ||
|
|
dcab83f936 | ||
|
|
b228e4ec26 | ||
|
|
4071a1301f | ||
|
|
5c9db10710 |
@@ -13,6 +13,7 @@
|
|||||||
<a href="#key-features">Features</a> •
|
<a href="#key-features">Features</a> •
|
||||||
<a href="#how-to-use">Usage</a> •
|
<a href="#how-to-use">Usage</a> •
|
||||||
<a href="#how-it-works">How</a> •
|
<a href="#how-it-works">How</a> •
|
||||||
|
<a href="#help-us-translate-wygiwyh">Translate</a> •
|
||||||
<a href="#caveats-and-warnings">Caveats and Warnings</a> •
|
<a href="#caveats-and-warnings">Caveats and Warnings</a> •
|
||||||
<a href="#built-with">Built with</a>
|
<a href="#built-with">Built with</a>
|
||||||
</p>
|
</p>
|
||||||
@@ -133,6 +134,14 @@ To create the first user, open the container's console using Unraid's UI, by cli
|
|||||||
|
|
||||||
Check out our [Wiki](https://github.com/eitchtee/WYGIWYH/wiki) for more information.
|
Check out our [Wiki](https://github.com/eitchtee/WYGIWYH/wiki) for more information.
|
||||||
|
|
||||||
|
# Help us translate WYGIWYH!
|
||||||
|
<a href="https://translations.herculino.com/engage/wygiwyh/">
|
||||||
|
<img src="https://translations.herculino.com/widget/wygiwyh/open-graph.png" alt="Translation status" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Login with your github account
|
||||||
|
|
||||||
# Caveats and Warnings
|
# Caveats and Warnings
|
||||||
|
|
||||||
- I'm not an accountant, some terms and even calculations might be wrong. Make sure to open an issue if you see anything that could be improved.
|
- I'm not an accountant, some terms and even calculations might be wrong. Make sure to open an issue if you see anything that could be improved.
|
||||||
|
|||||||
@@ -163,6 +163,7 @@ AUTH_USER_MODEL = "users.User"
|
|||||||
|
|
||||||
LANGUAGE_CODE = "en"
|
LANGUAGE_CODE = "en"
|
||||||
LANGUAGES = (
|
LANGUAGES = (
|
||||||
|
("de", "Deutsch"),
|
||||||
("en", "English"),
|
("en", "English"),
|
||||||
("nl", "Nederlands"),
|
("nl", "Nederlands"),
|
||||||
("pt-br", "Português (Brasil)"),
|
("pt-br", "Português (Brasil)"),
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ from django.utils import timezone
|
|||||||
|
|
||||||
from apps.currencies.exchange_rates.providers import (
|
from apps.currencies.exchange_rates.providers import (
|
||||||
SynthFinanceProvider,
|
SynthFinanceProvider,
|
||||||
|
SynthFinanceStockProvider,
|
||||||
CoinGeckoFreeProvider,
|
CoinGeckoFreeProvider,
|
||||||
CoinGeckoProProvider,
|
CoinGeckoProProvider,
|
||||||
|
TransitiveRateProvider,
|
||||||
)
|
)
|
||||||
from apps.currencies.models import ExchangeRateService, ExchangeRate, Currency
|
from apps.currencies.models import ExchangeRateService, ExchangeRate, Currency
|
||||||
|
|
||||||
@@ -17,8 +19,10 @@ logger = logging.getLogger(__name__)
|
|||||||
# Map service types to provider classes
|
# Map service types to provider classes
|
||||||
PROVIDER_MAPPING = {
|
PROVIDER_MAPPING = {
|
||||||
"synth_finance": SynthFinanceProvider,
|
"synth_finance": SynthFinanceProvider,
|
||||||
|
"synth_finance_stock": SynthFinanceStockProvider,
|
||||||
"coingecko_free": CoinGeckoFreeProvider,
|
"coingecko_free": CoinGeckoFreeProvider,
|
||||||
"coingecko_pro": CoinGeckoProProvider,
|
"coingecko_pro": CoinGeckoProProvider,
|
||||||
|
"transitive": TransitiveRateProvider,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ import time
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from typing import Tuple, List
|
from typing import Tuple, List, Optional, Dict
|
||||||
|
|
||||||
from django.db.models import QuerySet
|
from django.db.models import QuerySet
|
||||||
|
|
||||||
from apps.currencies.models import Currency
|
from apps.currencies.models import Currency, ExchangeRate
|
||||||
from apps.currencies.exchange_rates.base import ExchangeRateProvider
|
from apps.currencies.exchange_rates.base import ExchangeRateProvider
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -150,3 +150,159 @@ class CoinGeckoProProvider(CoinGeckoFreeProvider):
|
|||||||
super().__init__(api_key)
|
super().__init__(api_key)
|
||||||
self.session = requests.Session()
|
self.session = requests.Session()
|
||||||
self.session.headers.update({"x-cg-pro-api-key": api_key})
|
self.session.headers.update({"x-cg-pro-api-key": api_key})
|
||||||
|
|
||||||
|
|
||||||
|
class SynthFinanceStockProvider(ExchangeRateProvider):
|
||||||
|
"""Implementation for Synth Finance API Real-Time Prices endpoint (synthfinance.com)"""
|
||||||
|
|
||||||
|
BASE_URL = "https://api.synthfinance.com/tickers"
|
||||||
|
rates_inverted = True
|
||||||
|
|
||||||
|
def __init__(self, api_key: str = None):
|
||||||
|
super().__init__(api_key)
|
||||||
|
self.session = requests.Session()
|
||||||
|
self.session.headers.update(
|
||||||
|
{"Authorization": f"Bearer {self.api_key}", "accept": "application/json"}
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_rates(
|
||||||
|
self, target_currencies: QuerySet, exchange_currencies: set
|
||||||
|
) -> List[Tuple[Currency, Currency, Decimal]]:
|
||||||
|
results = []
|
||||||
|
|
||||||
|
for currency in target_currencies:
|
||||||
|
if currency.exchange_currency not in exchange_currencies:
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Same currency has rate of 1
|
||||||
|
if currency.code == currency.exchange_currency.code:
|
||||||
|
rate = Decimal("1")
|
||||||
|
results.append((currency.exchange_currency, currency, rate))
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Fetch real-time price for this ticker
|
||||||
|
response = self.session.get(
|
||||||
|
f"{self.BASE_URL}/{currency.code}/real-time"
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
# Use fair market value as the rate
|
||||||
|
rate = Decimal(data["data"]["fair_market_value"])
|
||||||
|
results.append((currency.exchange_currency, currency, rate))
|
||||||
|
|
||||||
|
# Log API usage
|
||||||
|
credits_used = data["meta"]["credits_used"]
|
||||||
|
credits_remaining = data["meta"]["credits_remaining"]
|
||||||
|
logger.info(
|
||||||
|
f"Synth Finance API call for {currency.code}: {credits_used} credits used, {credits_remaining} remaining"
|
||||||
|
)
|
||||||
|
except requests.RequestException as e:
|
||||||
|
logger.error(
|
||||||
|
f"Error fetching rate from Synth Finance API for ticker {currency.code}: {e}",
|
||||||
|
exc_info=True,
|
||||||
|
)
|
||||||
|
except KeyError as e:
|
||||||
|
logger.error(
|
||||||
|
f"Unexpected response structure from Synth Finance API for ticker {currency.code}: {e}",
|
||||||
|
exc_info=True,
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(
|
||||||
|
f"Unexpected error processing Synth Finance data for ticker {currency.code}: {e}",
|
||||||
|
exc_info=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
class TransitiveRateProvider(ExchangeRateProvider):
|
||||||
|
"""Calculates exchange rates through paths of existing rates"""
|
||||||
|
|
||||||
|
rates_inverted = True
|
||||||
|
|
||||||
|
def __init__(self, api_key: str = None):
|
||||||
|
super().__init__(api_key) # API key not needed but maintaining interface
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def requires_api_key(cls) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_rates(
|
||||||
|
self, target_currencies: QuerySet, exchange_currencies: set
|
||||||
|
) -> List[Tuple[Currency, Currency, Decimal]]:
|
||||||
|
results = []
|
||||||
|
|
||||||
|
# Get recent rates for building the graph
|
||||||
|
recent_rates = ExchangeRate.objects.all()
|
||||||
|
|
||||||
|
# Build currency graph
|
||||||
|
currency_graph = self._build_currency_graph(recent_rates)
|
||||||
|
|
||||||
|
for target in target_currencies:
|
||||||
|
if (
|
||||||
|
not target.exchange_currency
|
||||||
|
or target.exchange_currency not in exchange_currencies
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Find path and calculate rate
|
||||||
|
from_id = target.exchange_currency.id
|
||||||
|
to_id = target.id
|
||||||
|
|
||||||
|
path, rate = self._find_conversion_path(currency_graph, from_id, to_id)
|
||||||
|
|
||||||
|
if path and rate:
|
||||||
|
path_codes = [Currency.objects.get(id=cid).code for cid in path]
|
||||||
|
logger.info(
|
||||||
|
f"Found conversion path: {' -> '.join(path_codes)}, rate: {rate}"
|
||||||
|
)
|
||||||
|
results.append((target.exchange_currency, target, rate))
|
||||||
|
else:
|
||||||
|
logger.debug(
|
||||||
|
f"No conversion path found for {target.exchange_currency.code}->{target.code}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _build_currency_graph(rates) -> Dict[int, Dict[int, Decimal]]:
|
||||||
|
"""Build a graph representation of currency relationships"""
|
||||||
|
graph = {}
|
||||||
|
|
||||||
|
for rate in rates:
|
||||||
|
# Add both directions to make the graph bidirectional
|
||||||
|
if rate.from_currency_id not in graph:
|
||||||
|
graph[rate.from_currency_id] = {}
|
||||||
|
graph[rate.from_currency_id][rate.to_currency_id] = rate.rate
|
||||||
|
|
||||||
|
if rate.to_currency_id not in graph:
|
||||||
|
graph[rate.to_currency_id] = {}
|
||||||
|
graph[rate.to_currency_id][rate.from_currency_id] = Decimal("1") / rate.rate
|
||||||
|
|
||||||
|
return graph
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _find_conversion_path(
|
||||||
|
graph, from_id, to_id
|
||||||
|
) -> Tuple[Optional[list], Optional[Decimal]]:
|
||||||
|
"""Find the shortest path between currencies using breadth-first search"""
|
||||||
|
if from_id not in graph or to_id not in graph:
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
queue = [(from_id, [from_id], Decimal("1"))]
|
||||||
|
visited = {from_id}
|
||||||
|
|
||||||
|
while queue:
|
||||||
|
current, path, current_rate = queue.pop(0)
|
||||||
|
|
||||||
|
if current == to_id:
|
||||||
|
return path, current_rate
|
||||||
|
|
||||||
|
for neighbor, rate in graph.get(current, {}).items():
|
||||||
|
if neighbor not in visited:
|
||||||
|
visited.add(neighbor)
|
||||||
|
queue.append((neighbor, path + [neighbor], current_rate * rate))
|
||||||
|
|
||||||
|
return None, None
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-03-02 01:28
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('currencies', '0011_remove_exchangerateservice_fetch_interval_hours_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='exchangerateservice',
|
||||||
|
name='service_type',
|
||||||
|
field=models.CharField(choices=[('synth_finance', 'Synth Finance'), ('synth_finance_stock', 'Synth Finance Stock'), ('coingecko_free', 'CoinGecko (Demo/Free)'), ('coingecko_pro', 'CoinGecko (Pro)')], max_length=255, verbose_name='Service Type'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-03-02 01:58
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('currencies', '0012_alter_exchangerateservice_service_type'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='exchangerateservice',
|
||||||
|
name='service_type',
|
||||||
|
field=models.CharField(choices=[('synth_finance', 'Synth Finance'), ('synth_finance_stock', 'Synth Finance Stock'), ('coingecko_free', 'CoinGecko (Demo/Free)'), ('coingecko_pro', 'CoinGecko (Pro)'), ('transitive', 'Transitive (Calculated from Existing Rates)')], max_length=255, verbose_name='Service Type'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -92,8 +92,10 @@ class ExchangeRateService(models.Model):
|
|||||||
|
|
||||||
class ServiceType(models.TextChoices):
|
class ServiceType(models.TextChoices):
|
||||||
SYNTH_FINANCE = "synth_finance", "Synth Finance"
|
SYNTH_FINANCE = "synth_finance", "Synth Finance"
|
||||||
|
SYNTH_FINANCE_STOCK = "synth_finance_stock", "Synth Finance Stock"
|
||||||
COINGECKO_FREE = "coingecko_free", "CoinGecko (Demo/Free)"
|
COINGECKO_FREE = "coingecko_free", "CoinGecko (Demo/Free)"
|
||||||
COINGECKO_PRO = "coingecko_pro", "CoinGecko (Pro)"
|
COINGECKO_PRO = "coingecko_pro", "CoinGecko (Pro)"
|
||||||
|
TRANSITIVE = "transitive", "Transitive (Calculated from Existing Rates)"
|
||||||
|
|
||||||
class IntervalType(models.TextChoices):
|
class IntervalType(models.TextChoices):
|
||||||
ON = "on", _("On")
|
ON = "on", _("On")
|
||||||
@@ -204,11 +206,11 @@ class ExchangeRateService(models.Model):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
hours = int(self.fetch_interval)
|
hours = int(self.fetch_interval)
|
||||||
if hours < 0 or hours > 23:
|
if hours < 1 or hours > 24:
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
{
|
{
|
||||||
"fetch_interval": _(
|
"fetch_interval": _(
|
||||||
"'Every X hours' interval must be between 0 and 23."
|
"'Every X hours' interval must be between 1 and 24."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,13 +2,15 @@ from import_export import fields, resources, widgets
|
|||||||
|
|
||||||
from apps.accounts.models import Account
|
from apps.accounts.models import Account
|
||||||
from apps.currencies.models import Currency, ExchangeRate, ExchangeRateService
|
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):
|
class CurrencyResource(resources.ModelResource):
|
||||||
exchange_currency = fields.Field(
|
exchange_currency = fields.Field(
|
||||||
attribute="exchange_currency",
|
attribute="exchange_currency",
|
||||||
column_name="exchange_currency",
|
column_name="exchange_currency",
|
||||||
widget=widgets.ForeignKeyWidget(Currency, "name"),
|
widget=SkipMissingForeignKeyWidget(Currency, "name"),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -26,6 +28,9 @@ class ExchangeRateResource(resources.ModelResource):
|
|||||||
column_name="to_currency",
|
column_name="to_currency",
|
||||||
widget=widgets.ForeignKeyWidget(Currency, "name"),
|
widget=widgets.ForeignKeyWidget(Currency, "name"),
|
||||||
)
|
)
|
||||||
|
rate = fields.Field(
|
||||||
|
attribute="rate", column_name="rate", widget=UniversalDecimalWidget()
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ExchangeRate
|
model = ExchangeRate
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from import_export.widgets import ForeignKeyWidget
|
|||||||
|
|
||||||
from apps.dca.models import DCAStrategy, DCAEntry
|
from apps.dca.models import DCAStrategy, DCAEntry
|
||||||
from apps.currencies.models import Currency
|
from apps.currencies.models import Currency
|
||||||
|
from apps.export_app.widgets.numbers import UniversalDecimalWidget
|
||||||
|
|
||||||
|
|
||||||
class DCAStrategyResource(resources.ModelResource):
|
class DCAStrategyResource(resources.ModelResource):
|
||||||
@@ -22,5 +23,16 @@ class DCAStrategyResource(resources.ModelResource):
|
|||||||
|
|
||||||
|
|
||||||
class DCAEntryResource(resources.ModelResource):
|
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:
|
class Meta:
|
||||||
model = DCAEntry
|
model = DCAEntry
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ from apps.transactions.models import (
|
|||||||
RecurringTransaction,
|
RecurringTransaction,
|
||||||
InstallmentPlan,
|
InstallmentPlan,
|
||||||
)
|
)
|
||||||
|
from apps.export_app.widgets.numbers import UniversalDecimalWidget
|
||||||
|
|
||||||
|
|
||||||
class TransactionResource(resources.ModelResource):
|
class TransactionResource(resources.ModelResource):
|
||||||
@@ -44,6 +45,12 @@ class TransactionResource(resources.ModelResource):
|
|||||||
column_name="internal_id", attribute="internal_id"
|
column_name="internal_id", attribute="internal_id"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
amount = fields.Field(
|
||||||
|
attribute="amount",
|
||||||
|
column_name="amount",
|
||||||
|
widget=UniversalDecimalWidget(),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Transaction
|
model = Transaction
|
||||||
|
|
||||||
@@ -91,6 +98,12 @@ class RecurringTransactionResource(resources.ModelResource):
|
|||||||
widget=AutoCreateManyToManyWidget(TransactionEntity, field="name"),
|
widget=AutoCreateManyToManyWidget(TransactionEntity, field="name"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
amount = fields.Field(
|
||||||
|
attribute="amount",
|
||||||
|
column_name="amount",
|
||||||
|
widget=UniversalDecimalWidget(),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = RecurringTransaction
|
model = RecurringTransaction
|
||||||
|
|
||||||
@@ -120,5 +133,11 @@ class InstallmentPlanResource(resources.ModelResource):
|
|||||||
widget=AutoCreateManyToManyWidget(TransactionEntity, field="name"),
|
widget=AutoCreateManyToManyWidget(TransactionEntity, field="name"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
installment_amount = fields.Field(
|
||||||
|
attribute="installment_amount",
|
||||||
|
column_name="installment_amount",
|
||||||
|
widget=UniversalDecimalWidget(),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = InstallmentPlan
|
model = InstallmentPlan
|
||||||
|
|||||||
@@ -240,11 +240,6 @@ def process_imports(request, cleaned_data):
|
|||||||
dataset = Dataset()
|
dataset = Dataset()
|
||||||
dataset.load(content, format="csv")
|
dataset.load(content, format="csv")
|
||||||
|
|
||||||
# Debug logging
|
|
||||||
logger.debug(f"Importing {field_name}")
|
|
||||||
logger.debug(f"Headers: {dataset.headers}")
|
|
||||||
logger.debug(f"First row: {dataset[0] if len(dataset) > 0 else 'No data'}")
|
|
||||||
|
|
||||||
# Perform the import
|
# Perform the import
|
||||||
result = resource.import_data(
|
result = resource.import_data(
|
||||||
dataset,
|
dataset,
|
||||||
@@ -265,6 +260,8 @@ def process_imports(request, cleaned_data):
|
|||||||
raise ImportError(f"Error importing {field_name}: {str(e)}")
|
raise ImportError(f"Error importing {field_name}: {str(e)}")
|
||||||
|
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
|
files = {}
|
||||||
|
|
||||||
if zip_file := cleaned_data.get("zip_file"):
|
if zip_file := cleaned_data.get("zip_file"):
|
||||||
# Process ZIP file
|
# Process ZIP file
|
||||||
with zipfile.ZipFile(zip_file) as z:
|
with zipfile.ZipFile(zip_file) as z:
|
||||||
@@ -273,10 +270,12 @@ def process_imports(request, cleaned_data):
|
|||||||
with z.open(filename) as f:
|
with z.open(filename) as f:
|
||||||
content = f.read().decode("utf-8")
|
content = f.read().decode("utf-8")
|
||||||
|
|
||||||
for field_name, resource_class in import_order:
|
files[name] = content
|
||||||
if name == field_name:
|
|
||||||
import_dataset(content, resource_class, field_name)
|
for field_name, resource_class in import_order:
|
||||||
break
|
if field_name in files.keys():
|
||||||
|
content = files[field_name]
|
||||||
|
import_dataset(content, resource_class, field_name)
|
||||||
else:
|
else:
|
||||||
# Process individual files
|
# Process individual files
|
||||||
for field_name, resource_class in import_order:
|
for field_name, resource_class in import_order:
|
||||||
|
|||||||
@@ -9,3 +9,14 @@ class AutoCreateForeignKeyWidget(ForeignKeyWidget):
|
|||||||
except self.model.DoesNotExist:
|
except self.model.DoesNotExist:
|
||||||
return self.model.objects.create(name=value)
|
return self.model.objects.create(name=value)
|
||||||
return None
|
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
|
||||||
|
|||||||
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(",", ".")
|
||||||
@@ -29,4 +29,19 @@ urlpatterns = [
|
|||||||
views.category_sum_by_currency,
|
views.category_sum_by_currency,
|
||||||
name="category_sum_by_currency",
|
name="category_sum_by_currency",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"insights/category-overview/",
|
||||||
|
views.category_overview,
|
||||||
|
name="category_overview",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"insights/late-transactions/",
|
||||||
|
views.late_transactions,
|
||||||
|
name="insights_late_transactions",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"insights/latest-transactions/",
|
||||||
|
views.latest_transactions,
|
||||||
|
name="insights_latest_transactions",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ def get_category_sums_by_account(queryset, category=None):
|
|||||||
current_income=Coalesce(
|
current_income=Coalesce(
|
||||||
Sum(
|
Sum(
|
||||||
Case(
|
Case(
|
||||||
When(type="IN", then="amount"),
|
When(type="IN", is_paid=True, then="amount"),
|
||||||
When(is_paid=True, then="amount"),
|
|
||||||
default=Value(0),
|
default=Value(0),
|
||||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||||
)
|
)
|
||||||
@@ -26,8 +25,7 @@ def get_category_sums_by_account(queryset, category=None):
|
|||||||
current_expense=Coalesce(
|
current_expense=Coalesce(
|
||||||
Sum(
|
Sum(
|
||||||
Case(
|
Case(
|
||||||
When(type="EX", then=-F("amount")),
|
When(type="EX", is_paid=True, then=-F("amount")),
|
||||||
When(is_paid=True, then="amount"),
|
|
||||||
default=Value(0),
|
default=Value(0),
|
||||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||||
)
|
)
|
||||||
@@ -38,8 +36,7 @@ def get_category_sums_by_account(queryset, category=None):
|
|||||||
projected_income=Coalesce(
|
projected_income=Coalesce(
|
||||||
Sum(
|
Sum(
|
||||||
Case(
|
Case(
|
||||||
When(type="IN", then="amount"),
|
When(type="IN", is_paid=False, then="amount"),
|
||||||
When(is_paid=False, then="amount"),
|
|
||||||
default=Value(0),
|
default=Value(0),
|
||||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||||
)
|
)
|
||||||
@@ -50,8 +47,7 @@ def get_category_sums_by_account(queryset, category=None):
|
|||||||
projected_expense=Coalesce(
|
projected_expense=Coalesce(
|
||||||
Sum(
|
Sum(
|
||||||
Case(
|
Case(
|
||||||
When(type="EX", then=-F("amount")),
|
When(type="EX", is_paid=False, then=-F("amount")),
|
||||||
When(is_paid=False, then="amount"),
|
|
||||||
default=Value(0),
|
default=Value(0),
|
||||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||||
)
|
)
|
||||||
@@ -97,8 +93,7 @@ def get_category_sums_by_currency(queryset, category=None):
|
|||||||
current_income=Coalesce(
|
current_income=Coalesce(
|
||||||
Sum(
|
Sum(
|
||||||
Case(
|
Case(
|
||||||
When(type="IN", then="amount"),
|
When(type="IN", is_paid=True, then="amount"),
|
||||||
When(is_paid=True, then="amount"),
|
|
||||||
default=Value(0),
|
default=Value(0),
|
||||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||||
)
|
)
|
||||||
@@ -109,8 +104,7 @@ def get_category_sums_by_currency(queryset, category=None):
|
|||||||
current_expense=Coalesce(
|
current_expense=Coalesce(
|
||||||
Sum(
|
Sum(
|
||||||
Case(
|
Case(
|
||||||
When(type="EX", then=-F("amount")),
|
When(type="EX", is_paid=True, then=-F("amount")),
|
||||||
When(is_paid=True, then="amount"),
|
|
||||||
default=Value(0),
|
default=Value(0),
|
||||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||||
)
|
)
|
||||||
@@ -121,8 +115,7 @@ def get_category_sums_by_currency(queryset, category=None):
|
|||||||
projected_income=Coalesce(
|
projected_income=Coalesce(
|
||||||
Sum(
|
Sum(
|
||||||
Case(
|
Case(
|
||||||
When(type="IN", then="amount"),
|
When(type="IN", is_paid=False, then="amount"),
|
||||||
When(is_paid=False, then="amount"),
|
|
||||||
default=Value(0),
|
default=Value(0),
|
||||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||||
)
|
)
|
||||||
@@ -133,8 +126,7 @@ def get_category_sums_by_currency(queryset, category=None):
|
|||||||
projected_expense=Coalesce(
|
projected_expense=Coalesce(
|
||||||
Sum(
|
Sum(
|
||||||
Case(
|
Case(
|
||||||
When(type="EX", then=-F("amount")),
|
When(type="EX", is_paid=False, then=-F("amount")),
|
||||||
When(is_paid=False, then="amount"),
|
|
||||||
default=Value(0),
|
default=Value(0),
|
||||||
output_field=DecimalField(max_digits=42, decimal_places=30),
|
output_field=DecimalField(max_digits=42, decimal_places=30),
|
||||||
)
|
)
|
||||||
|
|||||||
165
app/apps/insights/utils/category_overview.py
Normal file
165
app/apps/insights/utils/category_overview.py
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.db.models import Sum, Case, When, Value, DecimalField
|
||||||
|
from django.db.models.functions import Coalesce
|
||||||
|
|
||||||
|
from apps.transactions.models import Transaction
|
||||||
|
from apps.currencies.models import Currency
|
||||||
|
from apps.currencies.utils.convert import convert
|
||||||
|
|
||||||
|
|
||||||
|
def get_categories_totals(transactions_queryset, ignore_empty=False):
|
||||||
|
# Get metrics for each category and currency in a single query
|
||||||
|
category_currency_metrics = (
|
||||||
|
transactions_queryset.values(
|
||||||
|
"category",
|
||||||
|
"category__name",
|
||||||
|
"account__currency",
|
||||||
|
"account__currency__code",
|
||||||
|
"account__currency__name",
|
||||||
|
"account__currency__decimal_places",
|
||||||
|
"account__currency__prefix",
|
||||||
|
"account__currency__suffix",
|
||||||
|
"account__currency__exchange_currency",
|
||||||
|
)
|
||||||
|
.annotate(
|
||||||
|
expense_current=Coalesce(
|
||||||
|
Sum(
|
||||||
|
Case(
|
||||||
|
When(
|
||||||
|
type=Transaction.Type.EXPENSE, is_paid=True, then="amount"
|
||||||
|
),
|
||||||
|
default=Value(0),
|
||||||
|
output_field=models.DecimalField(),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Decimal("0"),
|
||||||
|
),
|
||||||
|
expense_projected=Coalesce(
|
||||||
|
Sum(
|
||||||
|
Case(
|
||||||
|
When(
|
||||||
|
type=Transaction.Type.EXPENSE, is_paid=False, then="amount"
|
||||||
|
),
|
||||||
|
default=Value(0),
|
||||||
|
output_field=models.DecimalField(),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Decimal("0"),
|
||||||
|
),
|
||||||
|
income_current=Coalesce(
|
||||||
|
Sum(
|
||||||
|
Case(
|
||||||
|
When(type=Transaction.Type.INCOME, is_paid=True, then="amount"),
|
||||||
|
default=Value(0),
|
||||||
|
output_field=models.DecimalField(),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Decimal("0"),
|
||||||
|
),
|
||||||
|
income_projected=Coalesce(
|
||||||
|
Sum(
|
||||||
|
Case(
|
||||||
|
When(
|
||||||
|
type=Transaction.Type.INCOME, is_paid=False, then="amount"
|
||||||
|
),
|
||||||
|
default=Value(0),
|
||||||
|
output_field=models.DecimalField(),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Decimal("0"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.order_by("category__name")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Process the results to structure by category
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
for metric in category_currency_metrics:
|
||||||
|
# Skip empty categories if ignore_empty is True
|
||||||
|
if ignore_empty and all(
|
||||||
|
metric[field] == Decimal("0")
|
||||||
|
for field in [
|
||||||
|
"expense_current",
|
||||||
|
"expense_projected",
|
||||||
|
"income_current",
|
||||||
|
"income_projected",
|
||||||
|
]
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Calculate derived totals
|
||||||
|
total_current = metric["income_current"] - metric["expense_current"]
|
||||||
|
total_projected = metric["income_projected"] - metric["expense_projected"]
|
||||||
|
total_income = metric["income_current"] + metric["income_projected"]
|
||||||
|
total_expense = metric["expense_current"] + metric["expense_projected"]
|
||||||
|
total_final = total_current + total_projected
|
||||||
|
|
||||||
|
category_id = metric["category"]
|
||||||
|
currency_id = metric["account__currency"]
|
||||||
|
|
||||||
|
if category_id not in result:
|
||||||
|
result[category_id] = {"name": metric["category__name"], "currencies": {}}
|
||||||
|
|
||||||
|
# Add currency data
|
||||||
|
currency_data = {
|
||||||
|
"currency": {
|
||||||
|
"code": metric["account__currency__code"],
|
||||||
|
"name": metric["account__currency__name"],
|
||||||
|
"decimal_places": metric["account__currency__decimal_places"],
|
||||||
|
"prefix": metric["account__currency__prefix"],
|
||||||
|
"suffix": metric["account__currency__suffix"],
|
||||||
|
},
|
||||||
|
"expense_current": metric["expense_current"],
|
||||||
|
"expense_projected": metric["expense_projected"],
|
||||||
|
"total_expense": total_expense,
|
||||||
|
"income_current": metric["income_current"],
|
||||||
|
"income_projected": metric["income_projected"],
|
||||||
|
"total_income": total_income,
|
||||||
|
"total_current": total_current,
|
||||||
|
"total_projected": total_projected,
|
||||||
|
"total_final": total_final,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add exchanged values if exchange_currency exists
|
||||||
|
if metric["account__currency__exchange_currency"]:
|
||||||
|
from_currency = Currency.objects.get(id=currency_id)
|
||||||
|
exchange_currency = Currency.objects.get(
|
||||||
|
id=metric["account__currency__exchange_currency"]
|
||||||
|
)
|
||||||
|
|
||||||
|
exchanged = {}
|
||||||
|
for field in [
|
||||||
|
"expense_current",
|
||||||
|
"expense_projected",
|
||||||
|
"income_current",
|
||||||
|
"income_projected",
|
||||||
|
"total_income",
|
||||||
|
"total_expense",
|
||||||
|
"total_current",
|
||||||
|
"total_projected",
|
||||||
|
"total_final",
|
||||||
|
]:
|
||||||
|
amount, prefix, suffix, decimal_places = convert(
|
||||||
|
amount=currency_data[field],
|
||||||
|
from_currency=from_currency,
|
||||||
|
to_currency=exchange_currency,
|
||||||
|
)
|
||||||
|
if amount is not None:
|
||||||
|
exchanged[field] = amount
|
||||||
|
if "currency" not in exchanged:
|
||||||
|
exchanged["currency"] = {
|
||||||
|
"prefix": prefix,
|
||||||
|
"suffix": suffix,
|
||||||
|
"decimal_places": decimal_places,
|
||||||
|
"code": exchange_currency.code,
|
||||||
|
"name": exchange_currency.name,
|
||||||
|
}
|
||||||
|
if exchanged:
|
||||||
|
currency_data["exchanged"] = exchanged
|
||||||
|
|
||||||
|
result[category_id]["currencies"][currency_id] = currency_data
|
||||||
|
|
||||||
|
return result
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import decimal
|
||||||
|
import json
|
||||||
|
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
@@ -22,7 +25,8 @@ from apps.insights.utils.sankey import (
|
|||||||
generate_sankey_data_by_currency,
|
generate_sankey_data_by_currency,
|
||||||
)
|
)
|
||||||
from apps.insights.utils.transactions import get_transactions
|
from apps.insights.utils.transactions import get_transactions
|
||||||
from apps.transactions.models import TransactionCategory
|
from apps.transactions.models import TransactionCategory, Transaction
|
||||||
|
from apps.insights.utils.category_overview import get_categories_totals
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@@ -157,3 +161,51 @@ def category_sum_by_currency(request):
|
|||||||
"insights/fragments/category_explorer/charts/currency.html",
|
"insights/fragments/category_explorer/charts/currency.html",
|
||||||
{"currency_data": currency_data},
|
{"currency_data": currency_data},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@only_htmx
|
||||||
|
@login_required
|
||||||
|
@require_http_methods(["GET"])
|
||||||
|
def category_overview(request):
|
||||||
|
# Get filtered transactions
|
||||||
|
transactions = get_transactions(request, include_silent=True)
|
||||||
|
|
||||||
|
total_table = get_categories_totals(
|
||||||
|
transactions_queryset=transactions, ignore_empty=False
|
||||||
|
)
|
||||||
|
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"insights/fragments/category_overview/index.html",
|
||||||
|
{"total_table": total_table},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@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.models import Currency
|
||||||
from apps.currencies.utils.convert import convert
|
from apps.currencies.utils.convert import convert
|
||||||
from apps.mini_tools.forms import CurrencyConverterForm
|
from apps.mini_tools.forms import CurrencyConverterForm
|
||||||
|
from apps.mini_tools.utils.exchange_rate_map import get_currency_exchange_map
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@@ -14,11 +15,13 @@ def unit_price_calculator(request):
|
|||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def currency_converter(request):
|
def currency_converter(request):
|
||||||
|
rate_map = get_currency_exchange_map()
|
||||||
|
|
||||||
form = CurrencyConverterForm()
|
form = CurrencyConverterForm()
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"mini_tools/currency_converter/currency_converter.html",
|
"mini_tools/currency_converter/currency_converter.html",
|
||||||
context={"form": form},
|
context={"form": form, "rate_map": rate_map},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -297,10 +297,8 @@ class UpdateOrCreateTransactionRuleAction(models.Model):
|
|||||||
search_query = Q()
|
search_query = Q()
|
||||||
|
|
||||||
def add_to_query(field_name, value, operator):
|
def add_to_query(field_name, value, operator):
|
||||||
if isinstance(value, (int, str)):
|
lookup = f"{field_name}__{operator}"
|
||||||
lookup = f"{field_name}__{operator}"
|
return Q(**{lookup: value})
|
||||||
return Q(**{lookup: value})
|
|
||||||
return Q()
|
|
||||||
|
|
||||||
if self.search_account:
|
if self.search_account:
|
||||||
value = simple.eval(self.search_account)
|
value = simple.eval(self.search_account)
|
||||||
|
|||||||
@@ -131,14 +131,16 @@ def _process_update_or_create_transaction_action(action, simple_eval):
|
|||||||
|
|
||||||
# Build search query using the helper method
|
# Build search query using the helper method
|
||||||
search_query = action.build_search_query(simple_eval)
|
search_query = action.build_search_query(simple_eval)
|
||||||
|
logger.info("Searching transactions using: %s", search_query)
|
||||||
|
|
||||||
# Find latest matching transaction or create new
|
# Find latest matching transaction or create new
|
||||||
if search_query:
|
if search_query:
|
||||||
transaction = (
|
transactions = Transaction.objects.filter(search_query).order_by("-date", "-id")
|
||||||
Transaction.objects.filter(search_query).order_by("-date", "-id").first()
|
transaction = transactions.first()
|
||||||
)
|
logger.info("Found at least one matching transaction, using latest")
|
||||||
else:
|
else:
|
||||||
transaction = None
|
transaction = None
|
||||||
|
logger.info("No matching transaction found, creating a new transaction")
|
||||||
|
|
||||||
if not transaction:
|
if not transaction:
|
||||||
transaction = Transaction()
|
transaction = Transaction()
|
||||||
|
|||||||
@@ -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
2841
app/locale/en/LC_MESSAGES/django.po
Normal file
2841
app/locale/en/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,21 +3,21 @@
|
|||||||
# This file is distributed under the same license as the PACKAGE package.
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
#
|
#
|
||||||
#, fuzzy
|
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-02-19 13:44-0300\n"
|
"POT-Creation-Date: 2025-03-01 23:06-0300\n"
|
||||||
"PO-Revision-Date: 2025-02-12 06:58+0100\n"
|
"PO-Revision-Date: 2025-03-02 02:08+0000\n"
|
||||||
"Last-Translator: Dimitri Decrock <dimitri@fam-decrock.eu>\n"
|
"Last-Translator: Herculino Trotta <netotrotta@gmail.com>\n"
|
||||||
"Language-Team: \n"
|
"Language-Team: Dutch <https://translations.herculino.com/projects/wygiwyh/"
|
||||||
|
"app/nl/>\n"
|
||||||
"Language: nl\n"
|
"Language: nl\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||||
"X-Generator: Poedit 3.5\n"
|
"X-Generator: Weblate 5.10.1\n"
|
||||||
|
|
||||||
#: apps/accounts/forms.py:24
|
#: apps/accounts/forms.py:24
|
||||||
msgid "Group name"
|
msgid "Group name"
|
||||||
@@ -76,6 +76,7 @@ msgstr "Nieuw saldo"
|
|||||||
#: apps/transactions/forms.py:299 apps/transactions/forms.py:479
|
#: apps/transactions/forms.py:299 apps/transactions/forms.py:479
|
||||||
#: apps/transactions/forms.py:724 apps/transactions/models.py:203
|
#: apps/transactions/forms.py:724 apps/transactions/models.py:203
|
||||||
#: apps/transactions/models.py:378 apps/transactions/models.py:558
|
#: apps/transactions/models.py:378 apps/transactions/models.py:558
|
||||||
|
#: templates/insights/fragments/category_overview/index.html:9
|
||||||
msgid "Category"
|
msgid "Category"
|
||||||
msgstr "Categorie"
|
msgstr "Categorie"
|
||||||
|
|
||||||
@@ -156,7 +157,7 @@ msgstr "Gearchiveerd"
|
|||||||
msgid "Archived accounts don't show up nor count towards your net worth"
|
msgid "Archived accounts don't show up nor count towards your net worth"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Gearchiveerde rekeningen worden niet weergegeven en tellen niet mee voor je "
|
"Gearchiveerde rekeningen worden niet weergegeven en tellen niet mee voor je "
|
||||||
"\"Netto Waarde\"."
|
"\"Netto Waarde\""
|
||||||
|
|
||||||
#: apps/accounts/models.py:59 apps/rules/forms.py:160 apps/rules/forms.py:173
|
#: 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/rules/models.py:24 apps/rules/models.py:236
|
||||||
@@ -309,8 +310,8 @@ msgstr[1] "over %(years)s jaren"
|
|||||||
#, python-format
|
#, python-format
|
||||||
msgid "in %(months)s month"
|
msgid "in %(months)s month"
|
||||||
msgid_plural "in %(months)s months"
|
msgid_plural "in %(months)s months"
|
||||||
msgstr[0] "over %(months)s maand"
|
msgstr[0] "over %(months)s maand"
|
||||||
msgstr[1] "over %(months)s maanden"
|
msgstr[1] "over %(months)s maanden"
|
||||||
|
|
||||||
#: apps/common/templatetags/natural.py:56
|
#: apps/common/templatetags/natural.py:56
|
||||||
#, python-format
|
#, python-format
|
||||||
@@ -435,27 +436,27 @@ msgstr "Wisselkoersen"
|
|||||||
msgid "From and To currencies cannot be the same."
|
msgid "From and To currencies cannot be the same."
|
||||||
msgstr "Van en Naar munteenheid kunnen niet dezelfde zijn."
|
msgstr "Van en Naar munteenheid kunnen niet dezelfde zijn."
|
||||||
|
|
||||||
#: apps/currencies/models.py:99
|
#: apps/currencies/models.py:101
|
||||||
msgid "On"
|
msgid "On"
|
||||||
msgstr "Op"
|
msgstr "Op"
|
||||||
|
|
||||||
#: apps/currencies/models.py:100
|
#: apps/currencies/models.py:102
|
||||||
msgid "Every X hours"
|
msgid "Every X hours"
|
||||||
msgstr "Elke X Uren"
|
msgstr "Elke X Uren"
|
||||||
|
|
||||||
#: apps/currencies/models.py:101
|
#: apps/currencies/models.py:103
|
||||||
msgid "Not on"
|
msgid "Not on"
|
||||||
msgstr "Niet op"
|
msgstr "Niet op"
|
||||||
|
|
||||||
#: apps/currencies/models.py:103
|
#: apps/currencies/models.py:105
|
||||||
msgid "Service Name"
|
msgid "Service Name"
|
||||||
msgstr "Dienstnaam"
|
msgstr "Dienstnaam"
|
||||||
|
|
||||||
#: apps/currencies/models.py:105
|
#: apps/currencies/models.py:107
|
||||||
msgid "Service Type"
|
msgid "Service Type"
|
||||||
msgstr "Soort Dienst"
|
msgstr "Soort Dienst"
|
||||||
|
|
||||||
#: apps/currencies/models.py:107 apps/transactions/models.py:115
|
#: apps/currencies/models.py:109 apps/transactions/models.py:115
|
||||||
#: apps/transactions/models.py:134 apps/transactions/models.py:153
|
#: apps/transactions/models.py:134 apps/transactions/models.py:153
|
||||||
#: templates/categories/fragments/list.html:21
|
#: templates/categories/fragments/list.html:21
|
||||||
#: templates/entities/fragments/list.html:21
|
#: templates/entities/fragments/list.html:21
|
||||||
@@ -464,31 +465,31 @@ msgstr "Soort Dienst"
|
|||||||
msgid "Active"
|
msgid "Active"
|
||||||
msgstr "Actief"
|
msgstr "Actief"
|
||||||
|
|
||||||
#: apps/currencies/models.py:112
|
#: apps/currencies/models.py:114
|
||||||
msgid "API Key"
|
msgid "API Key"
|
||||||
msgstr "API Sleutel"
|
msgstr "API Sleutel"
|
||||||
|
|
||||||
#: apps/currencies/models.py:113
|
#: apps/currencies/models.py:115
|
||||||
msgid "API key for the service (if required)"
|
msgid "API key for the service (if required)"
|
||||||
msgstr "API sleutel voor de dienst (indien verplicht)"
|
msgstr "API sleutel voor de dienst (indien verplicht)"
|
||||||
|
|
||||||
#: apps/currencies/models.py:118
|
#: apps/currencies/models.py:120
|
||||||
msgid "Interval Type"
|
msgid "Interval Type"
|
||||||
msgstr "Soort Interval"
|
msgstr "Soort Interval"
|
||||||
|
|
||||||
#: apps/currencies/models.py:122
|
#: apps/currencies/models.py:124
|
||||||
msgid "Interval"
|
msgid "Interval"
|
||||||
msgstr "Interval"
|
msgstr "Interval"
|
||||||
|
|
||||||
#: apps/currencies/models.py:125
|
#: apps/currencies/models.py:127
|
||||||
msgid "Last Successful Fetch"
|
msgid "Last Successful Fetch"
|
||||||
msgstr "Laatste Succesvolle Ophaling"
|
msgstr "Laatste Succesvolle Ophaling"
|
||||||
|
|
||||||
#: apps/currencies/models.py:130
|
#: apps/currencies/models.py:132
|
||||||
msgid "Target Currencies"
|
msgid "Target Currencies"
|
||||||
msgstr "Doel Munteenheden"
|
msgstr "Doel Munteenheden"
|
||||||
|
|
||||||
#: apps/currencies/models.py:132
|
#: apps/currencies/models.py:134
|
||||||
msgid ""
|
msgid ""
|
||||||
"Select currencies to fetch exchange rates for. Rates will be fetched for "
|
"Select currencies to fetch exchange rates for. Rates will be fetched for "
|
||||||
"each currency against their set exchange currency."
|
"each currency against their set exchange currency."
|
||||||
@@ -496,11 +497,11 @@ msgstr ""
|
|||||||
"Selecteer munteenheden om wisselkoersen voor op te halen. De koersen worden "
|
"Selecteer munteenheden om wisselkoersen voor op te halen. De koersen worden "
|
||||||
"voor elke munteenheid opgehaald ten opzichte van de ingestelde wisselkoers."
|
"voor elke munteenheid opgehaald ten opzichte van de ingestelde wisselkoers."
|
||||||
|
|
||||||
#: apps/currencies/models.py:140
|
#: apps/currencies/models.py:142
|
||||||
msgid "Target Accounts"
|
msgid "Target Accounts"
|
||||||
msgstr "Naar rekeningen"
|
msgstr "Naar rekeningen"
|
||||||
|
|
||||||
#: apps/currencies/models.py:142
|
#: apps/currencies/models.py:144
|
||||||
msgid ""
|
msgid ""
|
||||||
"Select accounts to fetch exchange rates for. Rates will be fetched for each "
|
"Select accounts to fetch exchange rates for. Rates will be fetched for each "
|
||||||
"account's currency against their set exchange currency."
|
"account's currency against their set exchange currency."
|
||||||
@@ -509,23 +510,23 @@ msgstr ""
|
|||||||
"opgehaald voor de munteenheid van elke rekening ten opzichte van de "
|
"opgehaald voor de munteenheid van elke rekening ten opzichte van de "
|
||||||
"ingestelde wisselkoers."
|
"ingestelde wisselkoers."
|
||||||
|
|
||||||
#: apps/currencies/models.py:149
|
#: apps/currencies/models.py:151
|
||||||
msgid "Exchange Rate Service"
|
msgid "Exchange Rate Service"
|
||||||
msgstr "Wisselkoersdienst"
|
msgstr "Wisselkoersdienst"
|
||||||
|
|
||||||
#: apps/currencies/models.py:150
|
#: apps/currencies/models.py:152
|
||||||
msgid "Exchange Rate Services"
|
msgid "Exchange Rate Services"
|
||||||
msgstr "Wisselkoersdiensten"
|
msgstr "Wisselkoersdiensten"
|
||||||
|
|
||||||
#: apps/currencies/models.py:202
|
#: apps/currencies/models.py:204
|
||||||
msgid "'Every X hours' interval type requires a positive integer."
|
msgid "'Every X hours' interval type requires a positive integer."
|
||||||
msgstr "Voor het intervaltype ‘Elke X uur’ is een positief geheel getal nodig."
|
msgstr "Voor het intervaltype ‘Elke X uur’ is een positief geheel getal nodig."
|
||||||
|
|
||||||
#: apps/currencies/models.py:211
|
#: apps/currencies/models.py:213
|
||||||
msgid "'Every X hours' interval must be between 0 and 23."
|
msgid "'Every X hours' interval must be between 1 and 24."
|
||||||
msgstr "Het interval ‘Elke X uur’ moet tussen 0 en 23 liggen."
|
msgstr "Het interval ‘Elke X uur’ moet tussen 1 en 24 liggen."
|
||||||
|
|
||||||
#: apps/currencies/models.py:225
|
#: apps/currencies/models.py:227
|
||||||
msgid ""
|
msgid ""
|
||||||
"Invalid hour format. Use comma-separated hours (0-23) and/or ranges (e.g., "
|
"Invalid hour format. Use comma-separated hours (0-23) and/or ranges (e.g., "
|
||||||
"'1-5,8,10-12')."
|
"'1-5,8,10-12')."
|
||||||
@@ -533,7 +534,7 @@ msgstr ""
|
|||||||
"Ongeldige urennotatie. Gebruik door komma's gescheiden uren (0-23) en/of "
|
"Ongeldige urennotatie. Gebruik door komma's gescheiden uren (0-23) en/of "
|
||||||
"reeksen (bijv. ‘1-5,8,10-12’)."
|
"reeksen (bijv. ‘1-5,8,10-12’)."
|
||||||
|
|
||||||
#: apps/currencies/models.py:236
|
#: apps/currencies/models.py:238
|
||||||
msgid ""
|
msgid ""
|
||||||
"Invalid format. Please check the requirements for your selected interval "
|
"Invalid format. Please check the requirements for your selected interval "
|
||||||
"type."
|
"type."
|
||||||
@@ -582,10 +583,8 @@ msgid "Services queued successfully"
|
|||||||
msgstr "Diensten succesvol in de wachtrij geplaatst"
|
msgstr "Diensten succesvol in de wachtrij geplaatst"
|
||||||
|
|
||||||
#: apps/dca/forms.py:65 apps/dca/forms.py:164
|
#: apps/dca/forms.py:65 apps/dca/forms.py:164
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Deleted transactions"
|
|
||||||
msgid "Create transaction"
|
msgid "Create transaction"
|
||||||
msgstr "Verwijderde verrichtingen"
|
msgstr "Maak verrichtingen"
|
||||||
|
|
||||||
#: apps/dca/forms.py:70 apps/transactions/forms.py:267
|
#: apps/dca/forms.py:70 apps/transactions/forms.py:267
|
||||||
msgid "From Account"
|
msgid "From Account"
|
||||||
@@ -602,31 +601,29 @@ msgstr "Uitgave Transactie"
|
|||||||
#: apps/dca/forms.py:120 apps/dca/forms.py:130
|
#: apps/dca/forms.py:120 apps/dca/forms.py:130
|
||||||
msgid "Type to search for a transaction to link to this entry"
|
msgid "Type to search for a transaction to link to this entry"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Type om een transactie te zoeken die aan dit item moet worden gekoppeld"
|
||||||
|
|
||||||
#: apps/dca/forms.py:126 apps/dca/models.py:177
|
#: apps/dca/forms.py:126 apps/dca/models.py:177
|
||||||
msgid "Income Transaction"
|
msgid "Income Transaction"
|
||||||
msgstr "Ontvangsten Transactie"
|
msgstr "Ontvangsten Transactie"
|
||||||
|
|
||||||
#: apps/dca/forms.py:210
|
#: apps/dca/forms.py:210
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Edit transaction"
|
|
||||||
msgid "Link 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:279 apps/dca/forms.py:280 apps/dca/forms.py:285
|
||||||
#: apps/dca/forms.py:289
|
#: apps/dca/forms.py:289
|
||||||
msgid "You must provide an account."
|
msgid "You must provide an account."
|
||||||
msgstr ""
|
msgstr "Je moet een account opgeven."
|
||||||
|
|
||||||
#: apps/dca/forms.py:294 apps/transactions/forms.py:414
|
#: apps/dca/forms.py:294 apps/transactions/forms.py:414
|
||||||
msgid "From and To accounts must be different."
|
msgid "From and To accounts must be different."
|
||||||
msgstr "Van en Naar rekening moeten verschillend zijn."
|
msgstr "Van en Naar rekening moeten verschillend zijn."
|
||||||
|
|
||||||
#: apps/dca/forms.py:308
|
#: apps/dca/forms.py:308
|
||||||
#, fuzzy, python-format
|
#, python-format
|
||||||
#| msgid "DCA Strategies"
|
|
||||||
msgid "DCA for %(strategy_name)s"
|
msgid "DCA for %(strategy_name)s"
|
||||||
msgstr "DCA Strategieën"
|
msgstr "DCA voor %(strategy_name)s"
|
||||||
|
|
||||||
#: apps/dca/models.py:17
|
#: apps/dca/models.py:17
|
||||||
msgid "Target Currency"
|
msgid "Target Currency"
|
||||||
@@ -749,7 +746,7 @@ msgstr "Regels"
|
|||||||
|
|
||||||
#: apps/export_app/forms.py:80 templates/cotton/transaction/item.html:56
|
#: apps/export_app/forms.py:80 templates/cotton/transaction/item.html:56
|
||||||
msgid "DCA"
|
msgid "DCA"
|
||||||
msgstr ""
|
msgstr "DCA"
|
||||||
|
|
||||||
#: apps/export_app/forms.py:86 apps/export_app/forms.py:147
|
#: apps/export_app/forms.py:86 apps/export_app/forms.py:147
|
||||||
#: templates/import_app/fragments/profiles/list.html:5
|
#: templates/import_app/fragments/profiles/list.html:5
|
||||||
@@ -759,18 +756,16 @@ msgstr "Profielen importeren"
|
|||||||
|
|
||||||
#: apps/export_app/forms.py:112 templates/export_app/fragments/export.html:5
|
#: apps/export_app/forms.py:112 templates/export_app/fragments/export.html:5
|
||||||
#: templates/export_app/pages/index.html:15
|
#: templates/export_app/pages/index.html:15
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Import"
|
|
||||||
msgid "Export"
|
msgid "Export"
|
||||||
msgstr "Importeer"
|
msgstr "Exporteer"
|
||||||
|
|
||||||
#: apps/export_app/forms.py:121
|
#: apps/export_app/forms.py:121
|
||||||
msgid "Import a ZIP file exported from WYGIWYH"
|
msgid "Import a ZIP file exported from WYGIWYH"
|
||||||
msgstr ""
|
msgstr "Importeer een ZIP-bestand geëxporteerd vanuit WYGIWYH"
|
||||||
|
|
||||||
#: apps/export_app/forms.py:122
|
#: apps/export_app/forms.py:122
|
||||||
msgid "ZIP File"
|
msgid "ZIP File"
|
||||||
msgstr ""
|
msgstr "ZIP-bestand"
|
||||||
|
|
||||||
#: apps/export_app/forms.py:138 apps/rules/models.py:16
|
#: apps/export_app/forms.py:138 apps/rules/models.py:16
|
||||||
msgid "Transaction rules"
|
msgid "Transaction rules"
|
||||||
@@ -778,7 +773,7 @@ msgstr "Verrichtingsregels"
|
|||||||
|
|
||||||
#: apps/export_app/forms.py:140 apps/rules/models.py:53
|
#: apps/export_app/forms.py:140 apps/rules/models.py:53
|
||||||
msgid "Edit transaction action"
|
msgid "Edit transaction action"
|
||||||
msgstr "Bewerk verrichtingsregel actie"
|
msgstr "Bewerk verrichtingsactie"
|
||||||
|
|
||||||
#: apps/export_app/forms.py:143 apps/rules/models.py:290
|
#: apps/export_app/forms.py:143 apps/rules/models.py:290
|
||||||
msgid "Update or create transaction actions"
|
msgid "Update or create transaction actions"
|
||||||
@@ -793,22 +788,22 @@ msgstr "Herstel"
|
|||||||
|
|
||||||
#: apps/export_app/forms.py:187
|
#: apps/export_app/forms.py:187
|
||||||
msgid "Please upload either a ZIP file or at least one CSV file"
|
msgid "Please upload either a ZIP file or at least one CSV file"
|
||||||
msgstr ""
|
msgstr "Upload een ZIP-bestand of ten minste één CSV-bestand"
|
||||||
|
|
||||||
#: apps/export_app/views.py:169
|
#: apps/export_app/views.py:168
|
||||||
msgid "You have to select at least one export"
|
msgid "You have to select at least one export"
|
||||||
msgstr ""
|
msgstr "U moet ten minste één export selecteren"
|
||||||
|
|
||||||
#: apps/export_app/views.py:187
|
#: apps/export_app/views.py:186
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Tag updated successfully"
|
|
||||||
msgid "Data restored successfully"
|
msgid "Data restored successfully"
|
||||||
msgstr "Label succesvol bijgewerkt"
|
msgstr "Gegevens succesvol hersteld"
|
||||||
|
|
||||||
#: apps/export_app/views.py:199
|
#: apps/export_app/views.py:198
|
||||||
msgid ""
|
msgid ""
|
||||||
"There was an error restoring your data. Check the logs for more details."
|
"There was an error restoring your data. Check the logs for more details."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Er is een fout opgetreden bij het herstellen van uw gegevens. Controleer de "
|
||||||
|
"logboeken voor meer details."
|
||||||
|
|
||||||
#: apps/import_app/forms.py:49
|
#: apps/import_app/forms.py:49
|
||||||
msgid "Select a file"
|
msgid "Select a file"
|
||||||
@@ -886,22 +881,21 @@ msgstr "Run met succes verwijderd"
|
|||||||
|
|
||||||
#: apps/insights/forms.py:119 apps/insights/utils/sankey.py:36
|
#: apps/insights/forms.py:119 apps/insights/utils/sankey.py:36
|
||||||
#: apps/insights/utils/sankey.py:167
|
#: apps/insights/utils/sankey.py:167
|
||||||
#, fuzzy
|
#: templates/insights/fragments/category_overview/index.html:18
|
||||||
#| msgid "Categories"
|
|
||||||
msgid "Uncategorized"
|
msgid "Uncategorized"
|
||||||
msgstr "Categorieën"
|
msgstr "Ongecategoriseerd"
|
||||||
|
|
||||||
#: apps/insights/utils/category_explorer.py:70
|
#: apps/insights/utils/category_explorer.py:66
|
||||||
#: apps/insights/utils/category_explorer.py:153
|
#: apps/insights/utils/category_explorer.py:145
|
||||||
#: templates/cotton/ui/percentage_distribution.html:10
|
#: templates/cotton/ui/percentage_distribution.html:10
|
||||||
#: templates/cotton/ui/percentage_distribution.html:14
|
#: templates/cotton/ui/percentage_distribution.html:14
|
||||||
#: templates/insights/fragments/category_explorer/charts/account.html:60
|
#: templates/insights/fragments/category_explorer/charts/account.html:72
|
||||||
#: templates/insights/fragments/category_explorer/charts/currency.html:60
|
#: templates/insights/fragments/category_explorer/charts/currency.html:72
|
||||||
msgid "Current Income"
|
msgid "Current Income"
|
||||||
msgstr "Huidige inkomsten"
|
msgstr "Huidige inkomsten"
|
||||||
|
|
||||||
#: apps/insights/utils/category_explorer.py:74
|
#: apps/insights/utils/category_explorer.py:70
|
||||||
#: apps/insights/utils/category_explorer.py:157
|
#: apps/insights/utils/category_explorer.py:149
|
||||||
#: templates/cotton/ui/percentage_distribution.html:24
|
#: templates/cotton/ui/percentage_distribution.html:24
|
||||||
#: templates/cotton/ui/percentage_distribution.html:28
|
#: templates/cotton/ui/percentage_distribution.html:28
|
||||||
#: templates/insights/fragments/category_explorer/charts/account.html:66
|
#: templates/insights/fragments/category_explorer/charts/account.html:66
|
||||||
@@ -909,30 +903,28 @@ msgstr "Huidige inkomsten"
|
|||||||
msgid "Current Expenses"
|
msgid "Current Expenses"
|
||||||
msgstr "Huidige uitgaven"
|
msgstr "Huidige uitgaven"
|
||||||
|
|
||||||
#: apps/insights/utils/category_explorer.py:78
|
#: apps/insights/utils/category_explorer.py:74
|
||||||
#: apps/insights/utils/category_explorer.py:161
|
#: apps/insights/utils/category_explorer.py:153
|
||||||
#: templates/cotton/ui/percentage_distribution.html:3
|
#: templates/cotton/ui/percentage_distribution.html:3
|
||||||
#: templates/cotton/ui/percentage_distribution.html:7
|
#: templates/cotton/ui/percentage_distribution.html:7
|
||||||
#: templates/insights/fragments/category_explorer/charts/account.html:72
|
#: templates/insights/fragments/category_explorer/charts/account.html:78
|
||||||
#: templates/insights/fragments/category_explorer/charts/currency.html:72
|
#: templates/insights/fragments/category_explorer/charts/currency.html:78
|
||||||
msgid "Projected Income"
|
msgid "Projected Income"
|
||||||
msgstr "Verwachte inkomsten"
|
msgstr "Verwachte inkomsten"
|
||||||
|
|
||||||
#: apps/insights/utils/category_explorer.py:82
|
#: apps/insights/utils/category_explorer.py:78
|
||||||
#: apps/insights/utils/category_explorer.py:165
|
#: apps/insights/utils/category_explorer.py:157
|
||||||
#: templates/cotton/ui/percentage_distribution.html:17
|
#: templates/cotton/ui/percentage_distribution.html:17
|
||||||
#: templates/cotton/ui/percentage_distribution.html:21
|
#: templates/cotton/ui/percentage_distribution.html:21
|
||||||
#: templates/insights/fragments/category_explorer/charts/account.html:78
|
#: templates/insights/fragments/category_explorer/charts/account.html:60
|
||||||
#: templates/insights/fragments/category_explorer/charts/currency.html:78
|
#: templates/insights/fragments/category_explorer/charts/currency.html:60
|
||||||
msgid "Projected Expenses"
|
msgid "Projected Expenses"
|
||||||
msgstr "Verwachte uitgaven"
|
msgstr "Verwachte uitgaven"
|
||||||
|
|
||||||
#: apps/insights/utils/sankey.py:133 apps/insights/utils/sankey.py:134
|
#: apps/insights/utils/sankey.py:133 apps/insights/utils/sankey.py:134
|
||||||
#: apps/insights/utils/sankey.py:263 apps/insights/utils/sankey.py:264
|
#: apps/insights/utils/sankey.py:263 apps/insights/utils/sankey.py:264
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Save"
|
|
||||||
msgid "Saved"
|
msgid "Saved"
|
||||||
msgstr "Opslaan"
|
msgstr "Opgeslagen"
|
||||||
|
|
||||||
#: apps/rules/forms.py:20
|
#: apps/rules/forms.py:20
|
||||||
msgid "Run on creation"
|
msgid "Run on creation"
|
||||||
@@ -1085,13 +1077,12 @@ msgid "Filter"
|
|||||||
msgstr "Filter"
|
msgstr "Filter"
|
||||||
|
|
||||||
#: apps/rules/models.py:85
|
#: apps/rules/models.py:85
|
||||||
#, fuzzy
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"Generic expression to enable or disable execution. Should evaluate to True "
|
"Generic expression to enable or disable execution. Should evaluate to True "
|
||||||
"or False"
|
"or False"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Generieke expressie om uitvoering in of uit te schakelen. Moet evalueren "
|
"Generieke expressie om uitvoering in of uit te schakelen. Moet evalueren "
|
||||||
"naar True of False"
|
"naar Waar of Onwaar"
|
||||||
|
|
||||||
#: apps/rules/models.py:289
|
#: apps/rules/models.py:289
|
||||||
msgid "Update or create transaction action"
|
msgid "Update or create transaction action"
|
||||||
@@ -1211,7 +1202,7 @@ msgstr "De einddatum moet na de begindatum vallen"
|
|||||||
|
|
||||||
#: apps/transactions/models.py:112
|
#: apps/transactions/models.py:112
|
||||||
msgid "Mute"
|
msgid "Mute"
|
||||||
msgstr "Gedempt"
|
msgstr "Dempen"
|
||||||
|
|
||||||
#: apps/transactions/models.py:117
|
#: apps/transactions/models.py:117
|
||||||
msgid ""
|
msgid ""
|
||||||
@@ -1258,6 +1249,7 @@ msgstr "Bedrijf"
|
|||||||
#: templates/calendar_view/fragments/list.html:52
|
#: templates/calendar_view/fragments/list.html:52
|
||||||
#: templates/calendar_view/fragments/list.html:54
|
#: templates/calendar_view/fragments/list.html:54
|
||||||
#: templates/cotton/ui/quick_transactions_buttons.html:10
|
#: templates/cotton/ui/quick_transactions_buttons.html:10
|
||||||
|
#: templates/insights/fragments/category_overview/index.html:10
|
||||||
#: templates/monthly_overview/fragments/monthly_summary.html:39
|
#: templates/monthly_overview/fragments/monthly_summary.html:39
|
||||||
msgid "Income"
|
msgid "Income"
|
||||||
msgstr "Ontvangsten Transactie"
|
msgstr "Ontvangsten Transactie"
|
||||||
@@ -1268,8 +1260,9 @@ msgstr "Ontvangsten Transactie"
|
|||||||
#: templates/calendar_view/fragments/list.html:56
|
#: templates/calendar_view/fragments/list.html:56
|
||||||
#: templates/calendar_view/fragments/list.html:58
|
#: templates/calendar_view/fragments/list.html:58
|
||||||
#: templates/cotton/ui/quick_transactions_buttons.html:18
|
#: templates/cotton/ui/quick_transactions_buttons.html:18
|
||||||
|
#: templates/insights/fragments/category_overview/index.html:11
|
||||||
msgid "Expense"
|
msgid "Expense"
|
||||||
msgstr "Uitgave Transactie"
|
msgstr "Uitgave"
|
||||||
|
|
||||||
#: apps/transactions/models.py:225 apps/transactions/models.py:390
|
#: apps/transactions/models.py:225 apps/transactions/models.py:390
|
||||||
msgid "Installment Plan"
|
msgid "Installment Plan"
|
||||||
@@ -1296,16 +1289,12 @@ msgid "No tags"
|
|||||||
msgstr "Geen labels"
|
msgstr "Geen labels"
|
||||||
|
|
||||||
#: apps/transactions/models.py:324
|
#: apps/transactions/models.py:324
|
||||||
#, fuzzy
|
|
||||||
#| msgid "No categories"
|
|
||||||
msgid "No category"
|
msgid "No category"
|
||||||
msgstr "Geen categorieën"
|
msgstr "Geen categorie"
|
||||||
|
|
||||||
#: apps/transactions/models.py:326
|
#: apps/transactions/models.py:326
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Description"
|
|
||||||
msgid "No description"
|
msgid "No description"
|
||||||
msgstr "Beschrijving"
|
msgstr "Geen Beschrijving"
|
||||||
|
|
||||||
#: apps/transactions/models.py:332
|
#: apps/transactions/models.py:332
|
||||||
msgid "Yearly"
|
msgid "Yearly"
|
||||||
@@ -1897,6 +1886,7 @@ msgid "Muted"
|
|||||||
msgstr "Gedempt"
|
msgstr "Gedempt"
|
||||||
|
|
||||||
#: templates/categories/fragments/table.html:57
|
#: templates/categories/fragments/table.html:57
|
||||||
|
#: templates/insights/fragments/category_overview/index.html:67
|
||||||
msgid "No categories"
|
msgid "No categories"
|
||||||
msgstr "Geen categorieën"
|
msgstr "Geen categorieën"
|
||||||
|
|
||||||
@@ -1910,6 +1900,7 @@ msgstr "Sluiten"
|
|||||||
|
|
||||||
#: templates/cotton/config/search.html:6
|
#: templates/cotton/config/search.html:6
|
||||||
#: templates/import_app/fragments/profiles/list_presets.html:13
|
#: templates/import_app/fragments/profiles/list_presets.html:13
|
||||||
|
#: templates/monthly_overview/pages/overview.html:177
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr "Zoeken"
|
msgstr "Zoeken"
|
||||||
|
|
||||||
@@ -2255,7 +2246,7 @@ msgstr "Geen diensten ingesteld"
|
|||||||
|
|
||||||
#: templates/export_app/pages/index.html:4 templates/includes/navbar.html:136
|
#: templates/export_app/pages/index.html:4 templates/includes/navbar.html:136
|
||||||
msgid "Export and Restore"
|
msgid "Export and Restore"
|
||||||
msgstr ""
|
msgstr "Exporteren en Herstellen"
|
||||||
|
|
||||||
#: templates/import_app/fragments/profiles/add.html:6
|
#: templates/import_app/fragments/profiles/add.html:6
|
||||||
msgid "Add new import profile"
|
msgid "Add new import profile"
|
||||||
@@ -2360,7 +2351,7 @@ msgstr "Huidige"
|
|||||||
|
|
||||||
#: templates/includes/navbar.html:50
|
#: templates/includes/navbar.html:50
|
||||||
msgid "Insights"
|
msgid "Insights"
|
||||||
msgstr ""
|
msgstr "Inzichten"
|
||||||
|
|
||||||
#: templates/includes/navbar.html:66
|
#: templates/includes/navbar.html:66
|
||||||
msgid "Trash Can"
|
msgid "Trash Can"
|
||||||
@@ -2436,8 +2427,8 @@ msgstr "Annuleer"
|
|||||||
msgid "Confirm"
|
msgid "Confirm"
|
||||||
msgstr "Bevestig"
|
msgstr "Bevestig"
|
||||||
|
|
||||||
#: templates/insights/fragments/category_explorer/charts/account.html:99
|
#: templates/insights/fragments/category_explorer/charts/account.html:100
|
||||||
#: templates/insights/fragments/category_explorer/charts/currency.html:91
|
#: templates/insights/fragments/category_explorer/charts/currency.html:92
|
||||||
#: templates/monthly_overview/fragments/monthly_account_summary.html:14
|
#: templates/monthly_overview/fragments/monthly_account_summary.html:14
|
||||||
#: templates/monthly_overview/fragments/monthly_currency_summary.html:13
|
#: templates/monthly_overview/fragments/monthly_currency_summary.html:13
|
||||||
#: templates/transactions/fragments/all_account_summary.html:14
|
#: templates/transactions/fragments/all_account_summary.html:14
|
||||||
@@ -2451,27 +2442,40 @@ msgstr "Geen informatie om weer te geven"
|
|||||||
|
|
||||||
#: templates/insights/fragments/category_explorer/index.html:14
|
#: templates/insights/fragments/category_explorer/index.html:14
|
||||||
msgid "Income/Expense by Account"
|
msgid "Income/Expense by Account"
|
||||||
msgstr ""
|
msgstr "Inkomsten/uitgaven per rekening"
|
||||||
|
|
||||||
#: templates/insights/fragments/category_explorer/index.html:26
|
#: templates/insights/fragments/category_explorer/index.html:26
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Exchange Currency"
|
|
||||||
msgid "Income/Expense by Currency"
|
msgid "Income/Expense by Currency"
|
||||||
msgstr "Eenheid Wisselgeld"
|
msgstr "Inkomsten/uitgaven per Munteenheid"
|
||||||
|
|
||||||
|
#: templates/insights/fragments/category_overview/index.html:12
|
||||||
|
#: templates/monthly_overview/fragments/monthly_summary.html:167
|
||||||
|
msgid "Total"
|
||||||
|
msgstr "Totaal"
|
||||||
|
|
||||||
|
#: 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
|
#: templates/insights/fragments/sankey.html:93
|
||||||
msgid "From"
|
msgid "From"
|
||||||
msgstr ""
|
msgstr "Van"
|
||||||
|
|
||||||
#: templates/insights/fragments/sankey.html:96
|
#: templates/insights/fragments/sankey.html:96
|
||||||
msgid "Percentage"
|
msgid "Percentage"
|
||||||
msgstr ""
|
msgstr "Percentage"
|
||||||
|
|
||||||
#: templates/insights/pages/index.html:35
|
#: templates/insights/pages/index.html:35
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Monthly"
|
|
||||||
msgid "Month"
|
msgid "Month"
|
||||||
msgstr "Maandelijks"
|
msgstr "Maand"
|
||||||
|
|
||||||
#: templates/insights/pages/index.html:38
|
#: templates/insights/pages/index.html:38
|
||||||
#: templates/yearly_overview/pages/overview_by_account.html:61
|
#: templates/yearly_overview/pages/overview_by_account.html:61
|
||||||
@@ -2480,38 +2484,40 @@ msgid "Year"
|
|||||||
msgstr "Jaar"
|
msgstr "Jaar"
|
||||||
|
|
||||||
#: templates/insights/pages/index.html:43
|
#: templates/insights/pages/index.html:43
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Unchanged"
|
|
||||||
msgid "Month Range"
|
msgid "Month Range"
|
||||||
msgstr "Ongewijzigd"
|
msgstr "Maand Bereik"
|
||||||
|
|
||||||
#: templates/insights/pages/index.html:48
|
#: templates/insights/pages/index.html:48
|
||||||
msgid "Year Range"
|
msgid "Year Range"
|
||||||
msgstr ""
|
msgstr "Jaar Bereik"
|
||||||
|
|
||||||
#: templates/insights/pages/index.html:53
|
#: templates/insights/pages/index.html:53
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Date and Time"
|
|
||||||
msgid "Date Range"
|
msgid "Date Range"
|
||||||
msgstr "Datum en Tijd"
|
msgstr "Datum Bereik"
|
||||||
|
|
||||||
#: templates/insights/pages/index.html:82
|
#: templates/insights/pages/index.html:81
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Account"
|
|
||||||
msgid "Account Flow"
|
msgid "Account Flow"
|
||||||
msgstr "Rekening"
|
msgstr "Rekeningstroom"
|
||||||
|
|
||||||
#: templates/insights/pages/index.html:89
|
#: templates/insights/pages/index.html:88
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Currency Code"
|
|
||||||
msgid "Currency Flow"
|
msgid "Currency Flow"
|
||||||
msgstr "Munteenheids Code"
|
msgstr "Geldstroom"
|
||||||
|
|
||||||
#: templates/insights/pages/index.html:96
|
#: templates/insights/pages/index.html:95
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Category name"
|
|
||||||
msgid "Category Explorer"
|
msgid "Category Explorer"
|
||||||
msgstr "Naam van categorie"
|
msgstr "Categorie Verkenner"
|
||||||
|
|
||||||
|
#: templates/insights/pages/index.html:102
|
||||||
|
msgid "Categories Overview"
|
||||||
|
msgstr "Categorieën Overzicht"
|
||||||
|
|
||||||
|
#: templates/insights/pages/index.html:109
|
||||||
|
msgid "Late Transactions"
|
||||||
|
msgstr "Betalingsachterstanden"
|
||||||
|
|
||||||
|
#: templates/insights/pages/index.html:115
|
||||||
|
msgid "Latest Transactions"
|
||||||
|
msgstr "Laatste Verrichtingen"
|
||||||
|
|
||||||
#: templates/installment_plans/fragments/add.html:5
|
#: templates/installment_plans/fragments/add.html:5
|
||||||
msgid "Add installment plan"
|
msgid "Add installment plan"
|
||||||
@@ -2606,17 +2612,13 @@ msgstr "verwacht"
|
|||||||
msgid "Expenses"
|
msgid "Expenses"
|
||||||
msgstr "Uitgaven"
|
msgstr "Uitgaven"
|
||||||
|
|
||||||
#: templates/monthly_overview/fragments/monthly_summary.html:167
|
|
||||||
msgid "Total"
|
|
||||||
msgstr "Totaal"
|
|
||||||
|
|
||||||
#: templates/monthly_overview/fragments/monthly_summary.html:257
|
#: templates/monthly_overview/fragments/monthly_summary.html:257
|
||||||
msgid "Distribution"
|
msgid "Distribution"
|
||||||
msgstr "Verdeling"
|
msgstr "Verdeling"
|
||||||
|
|
||||||
#: templates/monthly_overview/pages/overview.html:68
|
#: templates/monthly_overview/pages/overview.html:68
|
||||||
msgid "Summary"
|
msgid "Summary"
|
||||||
msgstr "Overzicht"
|
msgstr "Samenvatting"
|
||||||
|
|
||||||
#: templates/monthly_overview/pages/overview.html:142
|
#: templates/monthly_overview/pages/overview.html:142
|
||||||
msgid "Filter transactions"
|
msgid "Filter transactions"
|
||||||
@@ -2875,6 +2877,26 @@ msgstr "Bedragen tonen"
|
|||||||
msgid "Yearly Overview"
|
msgid "Yearly Overview"
|
||||||
msgstr "Jaaroverzicht"
|
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
|
#, fuzzy
|
||||||
#~| msgid "Installment Plans"
|
#~| msgid "Installment Plans"
|
||||||
#~ msgid "Installment Planss"
|
#~ msgid "Installment Planss"
|
||||||
@@ -2935,11 +2957,6 @@ msgstr "Jaaroverzicht"
|
|||||||
#~ msgid "Reference Date Operator"
|
#~ msgid "Reference Date Operator"
|
||||||
#~ msgstr "Referentiedatum vanaf"
|
#~ msgstr "Referentiedatum vanaf"
|
||||||
|
|
||||||
#, fuzzy
|
|
||||||
#~| msgid "From Amount"
|
|
||||||
#~ msgid "Search Amount"
|
|
||||||
#~ msgstr "Van Bedrag"
|
|
||||||
|
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#~| msgid "Amount max"
|
#~| msgid "Amount max"
|
||||||
#~ msgid "Amount Operator"
|
#~ msgid "Amount Operator"
|
||||||
|
|||||||
@@ -3,21 +3,21 @@
|
|||||||
# This file is distributed under the same license as the PACKAGE package.
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
#
|
#
|
||||||
#, fuzzy
|
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-02-19 13:44-0300\n"
|
"POT-Creation-Date: 2025-03-01 23:06-0300\n"
|
||||||
"PO-Revision-Date: 2025-02-19 13:50-0300\n"
|
"PO-Revision-Date: 2025-03-02 02:08+0000\n"
|
||||||
"Last-Translator: Herculino Trotta\n"
|
"Last-Translator: Herculino Trotta <netotrotta@gmail.com>\n"
|
||||||
"Language-Team: \n"
|
"Language-Team: Portuguese (Brazil) <https://translations.herculino.com/"
|
||||||
|
"projects/wygiwyh/app/pt_BR/>\n"
|
||||||
"Language: pt_BR\n"
|
"Language: pt_BR\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
"Plural-Forms: nplurals=2; plural=n > 1;\n"
|
||||||
"X-Generator: Poedit 3.5\n"
|
"X-Generator: Weblate 5.10.1\n"
|
||||||
|
|
||||||
#: apps/accounts/forms.py:24
|
#: apps/accounts/forms.py:24
|
||||||
msgid "Group name"
|
msgid "Group name"
|
||||||
@@ -76,6 +76,7 @@ msgstr "Novo saldo"
|
|||||||
#: apps/transactions/forms.py:299 apps/transactions/forms.py:479
|
#: apps/transactions/forms.py:299 apps/transactions/forms.py:479
|
||||||
#: apps/transactions/forms.py:724 apps/transactions/models.py:203
|
#: apps/transactions/forms.py:724 apps/transactions/models.py:203
|
||||||
#: apps/transactions/models.py:378 apps/transactions/models.py:558
|
#: apps/transactions/models.py:378 apps/transactions/models.py:558
|
||||||
|
#: templates/insights/fragments/category_overview/index.html:9
|
||||||
msgid "Category"
|
msgid "Category"
|
||||||
msgstr "Categoria"
|
msgstr "Categoria"
|
||||||
|
|
||||||
@@ -433,27 +434,27 @@ msgstr "Taxas de Câmbio"
|
|||||||
msgid "From and To currencies cannot be the same."
|
msgid "From and To currencies cannot be the same."
|
||||||
msgstr "As moedas De e Para não podem ser as mesmas."
|
msgstr "As moedas De e Para não podem ser as mesmas."
|
||||||
|
|
||||||
#: apps/currencies/models.py:99
|
#: apps/currencies/models.py:101
|
||||||
msgid "On"
|
msgid "On"
|
||||||
msgstr "Em"
|
msgstr "Em"
|
||||||
|
|
||||||
#: apps/currencies/models.py:100
|
#: apps/currencies/models.py:102
|
||||||
msgid "Every X hours"
|
msgid "Every X hours"
|
||||||
msgstr "A cada X horas"
|
msgstr "A cada X horas"
|
||||||
|
|
||||||
#: apps/currencies/models.py:101
|
#: apps/currencies/models.py:103
|
||||||
msgid "Not on"
|
msgid "Not on"
|
||||||
msgstr "Não em"
|
msgstr "Não em"
|
||||||
|
|
||||||
#: apps/currencies/models.py:103
|
#: apps/currencies/models.py:105
|
||||||
msgid "Service Name"
|
msgid "Service Name"
|
||||||
msgstr "Nome do Serviço"
|
msgstr "Nome do Serviço"
|
||||||
|
|
||||||
#: apps/currencies/models.py:105
|
#: apps/currencies/models.py:107
|
||||||
msgid "Service Type"
|
msgid "Service Type"
|
||||||
msgstr "Tipo de Serviço"
|
msgstr "Tipo de Serviço"
|
||||||
|
|
||||||
#: apps/currencies/models.py:107 apps/transactions/models.py:115
|
#: apps/currencies/models.py:109 apps/transactions/models.py:115
|
||||||
#: apps/transactions/models.py:134 apps/transactions/models.py:153
|
#: apps/transactions/models.py:134 apps/transactions/models.py:153
|
||||||
#: templates/categories/fragments/list.html:21
|
#: templates/categories/fragments/list.html:21
|
||||||
#: templates/entities/fragments/list.html:21
|
#: templates/entities/fragments/list.html:21
|
||||||
@@ -462,31 +463,31 @@ msgstr "Tipo de Serviço"
|
|||||||
msgid "Active"
|
msgid "Active"
|
||||||
msgstr "Ativo"
|
msgstr "Ativo"
|
||||||
|
|
||||||
#: apps/currencies/models.py:112
|
#: apps/currencies/models.py:114
|
||||||
msgid "API Key"
|
msgid "API Key"
|
||||||
msgstr "Chave de API"
|
msgstr "Chave de API"
|
||||||
|
|
||||||
#: apps/currencies/models.py:113
|
#: apps/currencies/models.py:115
|
||||||
msgid "API key for the service (if required)"
|
msgid "API key for the service (if required)"
|
||||||
msgstr "Chave de API para o serviço (se necessário)"
|
msgstr "Chave de API para o serviço (se necessário)"
|
||||||
|
|
||||||
#: apps/currencies/models.py:118
|
#: apps/currencies/models.py:120
|
||||||
msgid "Interval Type"
|
msgid "Interval Type"
|
||||||
msgstr "Tipo de Intervalo"
|
msgstr "Tipo de Intervalo"
|
||||||
|
|
||||||
#: apps/currencies/models.py:122
|
#: apps/currencies/models.py:124
|
||||||
msgid "Interval"
|
msgid "Interval"
|
||||||
msgstr "Intervalo"
|
msgstr "Intervalo"
|
||||||
|
|
||||||
#: apps/currencies/models.py:125
|
#: apps/currencies/models.py:127
|
||||||
msgid "Last Successful Fetch"
|
msgid "Last Successful Fetch"
|
||||||
msgstr "Última execução bem-sucedida"
|
msgstr "Última execução bem-sucedida"
|
||||||
|
|
||||||
#: apps/currencies/models.py:130
|
#: apps/currencies/models.py:132
|
||||||
msgid "Target Currencies"
|
msgid "Target Currencies"
|
||||||
msgstr "Moedas-alvo"
|
msgstr "Moedas-alvo"
|
||||||
|
|
||||||
#: apps/currencies/models.py:132
|
#: apps/currencies/models.py:134
|
||||||
msgid ""
|
msgid ""
|
||||||
"Select currencies to fetch exchange rates for. Rates will be fetched for "
|
"Select currencies to fetch exchange rates for. Rates will be fetched for "
|
||||||
"each currency against their set exchange currency."
|
"each currency against their set exchange currency."
|
||||||
@@ -494,11 +495,11 @@ msgstr ""
|
|||||||
"Selecione as moedas para as quais deseja obter as taxas de câmbio. As taxas "
|
"Selecione as moedas para as quais deseja obter as taxas de câmbio. As taxas "
|
||||||
"serão obtidas para cada moeda em relação à moeda de câmbio definida."
|
"serão obtidas para cada moeda em relação à moeda de câmbio definida."
|
||||||
|
|
||||||
#: apps/currencies/models.py:140
|
#: apps/currencies/models.py:142
|
||||||
msgid "Target Accounts"
|
msgid "Target Accounts"
|
||||||
msgstr "Contas-alvo"
|
msgstr "Contas-alvo"
|
||||||
|
|
||||||
#: apps/currencies/models.py:142
|
#: apps/currencies/models.py:144
|
||||||
msgid ""
|
msgid ""
|
||||||
"Select accounts to fetch exchange rates for. Rates will be fetched for each "
|
"Select accounts to fetch exchange rates for. Rates will be fetched for each "
|
||||||
"account's currency against their set exchange currency."
|
"account's currency against their set exchange currency."
|
||||||
@@ -507,32 +508,32 @@ msgstr ""
|
|||||||
"serão obtidas para a moeda de cada conta em relação à moeda de câmbio "
|
"serão obtidas para a moeda de cada conta em relação à moeda de câmbio "
|
||||||
"definida."
|
"definida."
|
||||||
|
|
||||||
#: apps/currencies/models.py:149
|
#: apps/currencies/models.py:151
|
||||||
msgid "Exchange Rate Service"
|
msgid "Exchange Rate Service"
|
||||||
msgstr "Serviço de Taxa de Câmbio"
|
msgstr "Serviço de Taxa de Câmbio"
|
||||||
|
|
||||||
#: apps/currencies/models.py:150
|
#: apps/currencies/models.py:152
|
||||||
msgid "Exchange Rate Services"
|
msgid "Exchange Rate Services"
|
||||||
msgstr "Serviços de Taxa de Câmbio"
|
msgstr "Serviços de Taxa de Câmbio"
|
||||||
|
|
||||||
#: apps/currencies/models.py:202
|
#: apps/currencies/models.py:204
|
||||||
msgid "'Every X hours' interval type requires a positive integer."
|
msgid "'Every X hours' interval type requires a positive integer."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Intervalo do tipo 'A cada X horas' requerer um número inteiro positivo."
|
"Intervalo do tipo 'A cada X horas' requerer um número inteiro positivo."
|
||||||
|
|
||||||
#: apps/currencies/models.py:211
|
#: apps/currencies/models.py:213
|
||||||
msgid "'Every X hours' interval must be between 0 and 23."
|
msgid "'Every X hours' interval must be between 1 and 24."
|
||||||
msgstr "Intervalo do tipo 'A cada X horas' requerer um número entre 0 e 23."
|
msgstr "Intervalo do tipo 'A cada X horas' requerer um número entre 1 e 24."
|
||||||
|
|
||||||
#: apps/currencies/models.py:225
|
#: apps/currencies/models.py:227
|
||||||
msgid ""
|
msgid ""
|
||||||
"Invalid hour format. Use comma-separated hours (0-23) and/or ranges (e.g., "
|
"Invalid hour format. Use comma-separated hours (0-23) and/or ranges (e.g., "
|
||||||
"'1-5,8,10-12')."
|
"'1-5,8,10-12')."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Formato inválido de hora. Use uma lista de horas separada por vírgulas "
|
"Formato inválido de hora. Use uma lista de horas separada por vírgulas "
|
||||||
"(0-23) e/ou uma faixa (ex.: '1-5,8,10-12')"
|
"(0-23) e/ou uma faixa (ex.: '1-5,8,10-12')."
|
||||||
|
|
||||||
#: apps/currencies/models.py:236
|
#: apps/currencies/models.py:238
|
||||||
msgid ""
|
msgid ""
|
||||||
"Invalid format. Please check the requirements for your selected interval "
|
"Invalid format. Please check the requirements for your selected interval "
|
||||||
"type."
|
"type."
|
||||||
@@ -774,7 +775,7 @@ msgstr "Ação de editar de transação"
|
|||||||
|
|
||||||
#: apps/export_app/forms.py:143 apps/rules/models.py:290
|
#: apps/export_app/forms.py:143 apps/rules/models.py:290
|
||||||
msgid "Update or create transaction actions"
|
msgid "Update or create transaction actions"
|
||||||
msgstr "Ações de atualizar ou criar transação "
|
msgstr "Ações de atualizar ou criar transação"
|
||||||
|
|
||||||
#: apps/export_app/forms.py:176 templates/cotton/transaction/item.html:158
|
#: apps/export_app/forms.py:176 templates/cotton/transaction/item.html:158
|
||||||
#: templates/cotton/ui/deleted_transactions_action_bar.html:47
|
#: templates/cotton/ui/deleted_transactions_action_bar.html:47
|
||||||
@@ -787,15 +788,15 @@ msgstr "Restaurar"
|
|||||||
msgid "Please upload either a ZIP file or at least one CSV file"
|
msgid "Please upload either a ZIP file or at least one CSV file"
|
||||||
msgstr "Carregue um arquivo ZIP ou pelo menos um arquivo CSV"
|
msgstr "Carregue um arquivo ZIP ou pelo menos um arquivo CSV"
|
||||||
|
|
||||||
#: apps/export_app/views.py:169
|
#: apps/export_app/views.py:168
|
||||||
msgid "You have to select at least one export"
|
msgid "You have to select at least one export"
|
||||||
msgstr "É necessário selecionar pelo menos uma exportação"
|
msgstr "É necessário selecionar pelo menos uma exportação"
|
||||||
|
|
||||||
#: apps/export_app/views.py:187
|
#: apps/export_app/views.py:186
|
||||||
msgid "Data restored successfully"
|
msgid "Data restored successfully"
|
||||||
msgstr "Dados restaurados com sucesso"
|
msgstr "Dados restaurados com sucesso"
|
||||||
|
|
||||||
#: apps/export_app/views.py:199
|
#: apps/export_app/views.py:198
|
||||||
msgid ""
|
msgid ""
|
||||||
"There was an error restoring your data. Check the logs for more details."
|
"There was an error restoring your data. Check the logs for more details."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -878,20 +879,21 @@ msgstr "Importação apagada com sucesso"
|
|||||||
|
|
||||||
#: apps/insights/forms.py:119 apps/insights/utils/sankey.py:36
|
#: apps/insights/forms.py:119 apps/insights/utils/sankey.py:36
|
||||||
#: apps/insights/utils/sankey.py:167
|
#: apps/insights/utils/sankey.py:167
|
||||||
|
#: templates/insights/fragments/category_overview/index.html:18
|
||||||
msgid "Uncategorized"
|
msgid "Uncategorized"
|
||||||
msgstr "Sem categoria"
|
msgstr "Sem categoria"
|
||||||
|
|
||||||
#: apps/insights/utils/category_explorer.py:70
|
#: apps/insights/utils/category_explorer.py:66
|
||||||
#: apps/insights/utils/category_explorer.py:153
|
#: apps/insights/utils/category_explorer.py:145
|
||||||
#: templates/cotton/ui/percentage_distribution.html:10
|
#: templates/cotton/ui/percentage_distribution.html:10
|
||||||
#: templates/cotton/ui/percentage_distribution.html:14
|
#: templates/cotton/ui/percentage_distribution.html:14
|
||||||
#: templates/insights/fragments/category_explorer/charts/account.html:60
|
#: templates/insights/fragments/category_explorer/charts/account.html:72
|
||||||
#: templates/insights/fragments/category_explorer/charts/currency.html:60
|
#: templates/insights/fragments/category_explorer/charts/currency.html:72
|
||||||
msgid "Current Income"
|
msgid "Current Income"
|
||||||
msgstr "Renda Atual"
|
msgstr "Renda Atual"
|
||||||
|
|
||||||
#: apps/insights/utils/category_explorer.py:74
|
#: apps/insights/utils/category_explorer.py:70
|
||||||
#: apps/insights/utils/category_explorer.py:157
|
#: apps/insights/utils/category_explorer.py:149
|
||||||
#: templates/cotton/ui/percentage_distribution.html:24
|
#: templates/cotton/ui/percentage_distribution.html:24
|
||||||
#: templates/cotton/ui/percentage_distribution.html:28
|
#: templates/cotton/ui/percentage_distribution.html:28
|
||||||
#: templates/insights/fragments/category_explorer/charts/account.html:66
|
#: templates/insights/fragments/category_explorer/charts/account.html:66
|
||||||
@@ -899,21 +901,21 @@ msgstr "Renda Atual"
|
|||||||
msgid "Current Expenses"
|
msgid "Current Expenses"
|
||||||
msgstr "Despesas Atuais"
|
msgstr "Despesas Atuais"
|
||||||
|
|
||||||
#: apps/insights/utils/category_explorer.py:78
|
#: apps/insights/utils/category_explorer.py:74
|
||||||
#: apps/insights/utils/category_explorer.py:161
|
#: apps/insights/utils/category_explorer.py:153
|
||||||
#: templates/cotton/ui/percentage_distribution.html:3
|
#: templates/cotton/ui/percentage_distribution.html:3
|
||||||
#: templates/cotton/ui/percentage_distribution.html:7
|
#: templates/cotton/ui/percentage_distribution.html:7
|
||||||
#: templates/insights/fragments/category_explorer/charts/account.html:72
|
#: templates/insights/fragments/category_explorer/charts/account.html:78
|
||||||
#: templates/insights/fragments/category_explorer/charts/currency.html:72
|
#: templates/insights/fragments/category_explorer/charts/currency.html:78
|
||||||
msgid "Projected Income"
|
msgid "Projected Income"
|
||||||
msgstr "Renda Prevista"
|
msgstr "Renda Prevista"
|
||||||
|
|
||||||
#: apps/insights/utils/category_explorer.py:82
|
#: apps/insights/utils/category_explorer.py:78
|
||||||
#: apps/insights/utils/category_explorer.py:165
|
#: apps/insights/utils/category_explorer.py:157
|
||||||
#: templates/cotton/ui/percentage_distribution.html:17
|
#: templates/cotton/ui/percentage_distribution.html:17
|
||||||
#: templates/cotton/ui/percentage_distribution.html:21
|
#: templates/cotton/ui/percentage_distribution.html:21
|
||||||
#: templates/insights/fragments/category_explorer/charts/account.html:78
|
#: templates/insights/fragments/category_explorer/charts/account.html:60
|
||||||
#: templates/insights/fragments/category_explorer/charts/currency.html:78
|
#: templates/insights/fragments/category_explorer/charts/currency.html:60
|
||||||
msgid "Projected Expenses"
|
msgid "Projected Expenses"
|
||||||
msgstr "Despesas Previstas"
|
msgstr "Despesas Previstas"
|
||||||
|
|
||||||
@@ -1082,7 +1084,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: apps/rules/models.py:289
|
#: apps/rules/models.py:289
|
||||||
msgid "Update or create transaction action"
|
msgid "Update or create transaction action"
|
||||||
msgstr " Ação de atualizar ou criar transação"
|
msgstr "Ação de atualizar ou criar transação"
|
||||||
|
|
||||||
#: apps/rules/views.py:52
|
#: apps/rules/views.py:52
|
||||||
msgid "Rule deactivated successfully"
|
msgid "Rule deactivated successfully"
|
||||||
@@ -1244,6 +1246,7 @@ msgstr "Entidade"
|
|||||||
#: templates/calendar_view/fragments/list.html:52
|
#: templates/calendar_view/fragments/list.html:52
|
||||||
#: templates/calendar_view/fragments/list.html:54
|
#: templates/calendar_view/fragments/list.html:54
|
||||||
#: templates/cotton/ui/quick_transactions_buttons.html:10
|
#: templates/cotton/ui/quick_transactions_buttons.html:10
|
||||||
|
#: templates/insights/fragments/category_overview/index.html:10
|
||||||
#: templates/monthly_overview/fragments/monthly_summary.html:39
|
#: templates/monthly_overview/fragments/monthly_summary.html:39
|
||||||
msgid "Income"
|
msgid "Income"
|
||||||
msgstr "Renda"
|
msgstr "Renda"
|
||||||
@@ -1254,6 +1257,7 @@ msgstr "Renda"
|
|||||||
#: templates/calendar_view/fragments/list.html:56
|
#: templates/calendar_view/fragments/list.html:56
|
||||||
#: templates/calendar_view/fragments/list.html:58
|
#: templates/calendar_view/fragments/list.html:58
|
||||||
#: templates/cotton/ui/quick_transactions_buttons.html:18
|
#: templates/cotton/ui/quick_transactions_buttons.html:18
|
||||||
|
#: templates/insights/fragments/category_overview/index.html:11
|
||||||
msgid "Expense"
|
msgid "Expense"
|
||||||
msgstr "Despesa"
|
msgstr "Despesa"
|
||||||
|
|
||||||
@@ -1879,6 +1883,7 @@ msgid "Muted"
|
|||||||
msgstr "Silenciada"
|
msgstr "Silenciada"
|
||||||
|
|
||||||
#: templates/categories/fragments/table.html:57
|
#: templates/categories/fragments/table.html:57
|
||||||
|
#: templates/insights/fragments/category_overview/index.html:67
|
||||||
msgid "No categories"
|
msgid "No categories"
|
||||||
msgstr "Nenhum categoria"
|
msgstr "Nenhum categoria"
|
||||||
|
|
||||||
@@ -1892,6 +1897,7 @@ msgstr "Fechar"
|
|||||||
|
|
||||||
#: templates/cotton/config/search.html:6
|
#: templates/cotton/config/search.html:6
|
||||||
#: templates/import_app/fragments/profiles/list_presets.html:13
|
#: templates/import_app/fragments/profiles/list_presets.html:13
|
||||||
|
#: templates/monthly_overview/pages/overview.html:177
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr "Buscar"
|
msgstr "Buscar"
|
||||||
|
|
||||||
@@ -2419,8 +2425,8 @@ msgstr "Cancelar"
|
|||||||
msgid "Confirm"
|
msgid "Confirm"
|
||||||
msgstr "Confirmar"
|
msgstr "Confirmar"
|
||||||
|
|
||||||
#: templates/insights/fragments/category_explorer/charts/account.html:99
|
#: templates/insights/fragments/category_explorer/charts/account.html:100
|
||||||
#: templates/insights/fragments/category_explorer/charts/currency.html:91
|
#: templates/insights/fragments/category_explorer/charts/currency.html:92
|
||||||
#: templates/monthly_overview/fragments/monthly_account_summary.html:14
|
#: templates/monthly_overview/fragments/monthly_account_summary.html:14
|
||||||
#: templates/monthly_overview/fragments/monthly_currency_summary.html:13
|
#: templates/monthly_overview/fragments/monthly_currency_summary.html:13
|
||||||
#: templates/transactions/fragments/all_account_summary.html:14
|
#: templates/transactions/fragments/all_account_summary.html:14
|
||||||
@@ -2440,6 +2446,23 @@ msgstr "Gasto/Despesa por Conta"
|
|||||||
msgid "Income/Expense by Currency"
|
msgid "Income/Expense by Currency"
|
||||||
msgstr "Gasto/Despesa por Moeda"
|
msgstr "Gasto/Despesa por Moeda"
|
||||||
|
|
||||||
|
#: templates/insights/fragments/category_overview/index.html:12
|
||||||
|
#: templates/monthly_overview/fragments/monthly_summary.html:167
|
||||||
|
msgid "Total"
|
||||||
|
msgstr "Total"
|
||||||
|
|
||||||
|
#: 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
|
#: templates/insights/fragments/sankey.html:93
|
||||||
msgid "From"
|
msgid "From"
|
||||||
msgstr "De"
|
msgstr "De"
|
||||||
@@ -2470,18 +2493,30 @@ msgstr "Intervalo de Ano"
|
|||||||
msgid "Date Range"
|
msgid "Date Range"
|
||||||
msgstr "Intervalo de Data"
|
msgstr "Intervalo de Data"
|
||||||
|
|
||||||
#: templates/insights/pages/index.html:82
|
#: templates/insights/pages/index.html:81
|
||||||
msgid "Account Flow"
|
msgid "Account Flow"
|
||||||
msgstr "Fluxo de Conta"
|
msgstr "Fluxo de Conta"
|
||||||
|
|
||||||
#: templates/insights/pages/index.html:89
|
#: templates/insights/pages/index.html:88
|
||||||
msgid "Currency Flow"
|
msgid "Currency Flow"
|
||||||
msgstr "Fluxo de Moeda"
|
msgstr "Fluxo de Moeda"
|
||||||
|
|
||||||
#: templates/insights/pages/index.html:96
|
#: templates/insights/pages/index.html:95
|
||||||
msgid "Category Explorer"
|
msgid "Category Explorer"
|
||||||
msgstr "Explorador de Categoria"
|
msgstr "Explorador de Categoria"
|
||||||
|
|
||||||
|
#: templates/insights/pages/index.html:102
|
||||||
|
msgid "Categories Overview"
|
||||||
|
msgstr "Visão geral das categorias"
|
||||||
|
|
||||||
|
#: templates/insights/pages/index.html:109
|
||||||
|
msgid "Late Transactions"
|
||||||
|
msgstr "Transações Atrasadas"
|
||||||
|
|
||||||
|
#: templates/insights/pages/index.html:115
|
||||||
|
msgid "Latest Transactions"
|
||||||
|
msgstr "Últimas Transações"
|
||||||
|
|
||||||
#: templates/installment_plans/fragments/add.html:5
|
#: templates/installment_plans/fragments/add.html:5
|
||||||
msgid "Add installment plan"
|
msgid "Add installment plan"
|
||||||
msgstr "Adicionar parcelamento"
|
msgstr "Adicionar parcelamento"
|
||||||
@@ -2575,10 +2610,6 @@ msgstr "previsto"
|
|||||||
msgid "Expenses"
|
msgid "Expenses"
|
||||||
msgstr "Despesas"
|
msgstr "Despesas"
|
||||||
|
|
||||||
#: templates/monthly_overview/fragments/monthly_summary.html:167
|
|
||||||
msgid "Total"
|
|
||||||
msgstr "Total"
|
|
||||||
|
|
||||||
#: templates/monthly_overview/fragments/monthly_summary.html:257
|
#: templates/monthly_overview/fragments/monthly_summary.html:257
|
||||||
msgid "Distribution"
|
msgid "Distribution"
|
||||||
msgstr "Distribuição"
|
msgstr "Distribuição"
|
||||||
@@ -2841,6 +2872,26 @@ msgstr "Mostrar valores"
|
|||||||
msgid "Yearly Overview"
|
msgid "Yearly Overview"
|
||||||
msgstr "Visão Anual"
|
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
|
#, fuzzy
|
||||||
#~| msgid "Installment Plans"
|
#~| msgid "Installment Plans"
|
||||||
#~ msgid "Installment Planss"
|
#~ msgid "Installment Planss"
|
||||||
@@ -2906,11 +2957,6 @@ msgstr "Visão Anual"
|
|||||||
#~ msgid "Reference Date Operator"
|
#~ msgid "Reference Date Operator"
|
||||||
#~ msgstr "Data de Referência de"
|
#~ msgstr "Data de Referência de"
|
||||||
|
|
||||||
#, fuzzy
|
|
||||||
#~| msgid "From Amount"
|
|
||||||
#~ msgid "Search Amount"
|
|
||||||
#~ msgstr "Quantia de origem"
|
|
||||||
|
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#~| msgid "Amount max"
|
#~| msgid "Amount max"
|
||||||
#~ msgid "Amount Operator"
|
#~ msgid "Amount Operator"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<div class="row {% if not remove_padding %}p-5{% endif %}">
|
<div class="row {% if not remove_padding %}p-5{% endif %}">
|
||||||
<div class="col {% if not remove_padding %}p-5{% endif %}">
|
<div class="col {% if not remove_padding %}p-5{% endif %}">
|
||||||
<div class="text-center">
|
<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="lead mt-4 mb-0">{{ title }}</p>
|
||||||
<p class="tw-text-gray-500">{{ subtitle }}</p>
|
<p class="tw-text-gray-500">{{ subtitle }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,9 +15,9 @@
|
|||||||
_="on mouseover remove .tw-invisible from the first .transaction-actions in me end
|
_="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">
|
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="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 %}
|
{% 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 %}"
|
title="{% if transaction.is_paid %}{% trans 'Paid' %}{% else %}{% trans 'Projected' %}{% endif %}"
|
||||||
role="button"
|
role="button"
|
||||||
hx-get="{% url 'transaction_pay' transaction_id=transaction.id %}"
|
hx-get="{% url 'transaction_pay' transaction_id=transaction.id %}"
|
||||||
@@ -27,14 +27,14 @@
|
|||||||
class="fa-regular fa-circle"></i>{% endif %}
|
class="fa-regular fa-circle"></i>{% endif %}
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
{% 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 %}">
|
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
|
{% if transaction.is_paid %}<i class="fa-regular fa-circle-check"></i>{% else %}<i
|
||||||
class="fa-regular fa-circle"></i>{% endif %}
|
class="fa-regular fa-circle"></i>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-8 col-12">
|
<div class="col-lg col-12">
|
||||||
{# Date#}
|
{# Date#}
|
||||||
<div class="row mb-2 mb-lg-1 tw-text-gray-400">
|
<div class="row mb-2 mb-lg-1 tw-text-gray-400">
|
||||||
<div class="col-auto pe-1"><i class="fa-solid fa-calendar fa-fw me-1 fa-xs"></i></div>
|
<div class="col-auto pe-1"><i class="fa-solid fa-calendar fa-fw me-1 fa-xs"></i></div>
|
||||||
@@ -92,7 +92,7 @@
|
|||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<div class="main-amount mb-2 mb-lg-0">
|
||||||
<c-amount.display
|
<c-amount.display
|
||||||
:amount="transaction.amount"
|
:amount="transaction.amount"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<div class="card tw-relative h-100 shadow">
|
<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">
|
<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>
|
||||||
<div class="card-body">
|
<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>
|
<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>
|
||||||
|
|||||||
@@ -57,9 +57,9 @@
|
|||||||
labels: accountData.labels,
|
labels: accountData.labels,
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
label: "{% trans 'Current Income' %}",
|
label: "{% trans 'Projected Expenses' %}",
|
||||||
data: accountData.datasets[0].data,
|
data: accountData.datasets[3].data,
|
||||||
backgroundColor: '#4dde80',
|
backgroundColor: '#f8717180', // Added transparency
|
||||||
stack: 'stack0'
|
stack: 'stack0'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -68,18 +68,19 @@
|
|||||||
backgroundColor: '#f87171',
|
backgroundColor: '#f87171',
|
||||||
stack: 'stack0'
|
stack: 'stack0'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "{% trans 'Current Income' %}",
|
||||||
|
data: accountData.datasets[0].data,
|
||||||
|
backgroundColor: '#4dde80',
|
||||||
|
stack: 'stack0'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "{% trans 'Projected Income' %}",
|
label: "{% trans 'Projected Income' %}",
|
||||||
data: accountData.datasets[2].data,
|
data: accountData.datasets[2].data,
|
||||||
backgroundColor: '#4dde8080', // Added transparency
|
backgroundColor: '#4dde8080', // Added transparency
|
||||||
stack: 'stack0'
|
stack: 'stack0'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: "{% trans 'Projected Expenses' %}",
|
|
||||||
data: accountData.datasets[3].data,
|
|
||||||
backgroundColor: '#f8717180', // Added transparency
|
|
||||||
stack: 'stack0'
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
|
|||||||
@@ -57,9 +57,9 @@
|
|||||||
labels: currencyData.labels,
|
labels: currencyData.labels,
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
label: "{% trans 'Current Income' %}",
|
label: "{% trans 'Projected Expenses' %}",
|
||||||
data: currencyData.datasets[0].data,
|
data: currencyData.datasets[3].data,
|
||||||
backgroundColor: '#4dde80',
|
backgroundColor: '#f8717180', // Added transparency
|
||||||
stack: 'stack0'
|
stack: 'stack0'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -68,18 +68,19 @@
|
|||||||
backgroundColor: '#f87171',
|
backgroundColor: '#f87171',
|
||||||
stack: 'stack0'
|
stack: 'stack0'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "{% trans 'Current Income' %}",
|
||||||
|
data: currencyData.datasets[0].data,
|
||||||
|
backgroundColor: '#4dde80',
|
||||||
|
stack: 'stack0'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "{% trans 'Projected Income' %}",
|
label: "{% trans 'Projected Income' %}",
|
||||||
data: currencyData.datasets[2].data,
|
data: currencyData.datasets[2].data,
|
||||||
backgroundColor: '#4dde8080', // Added transparency
|
backgroundColor: '#4dde8080', // Added transparency
|
||||||
stack: 'stack0'
|
stack: 'stack0'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: "{% trans 'Projected Expenses' %}",
|
|
||||||
data: currencyData.datasets[3].data,
|
|
||||||
backgroundColor: '#f8717180', // Added transparency
|
|
||||||
stack: 'stack0'
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
options: chartOptions
|
options: chartOptions
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
<div hx-get="{% url 'category_overview' %}" hx-trigger="updated from:window" class="show-loading" hx-swap="outerHTML" hx-include="#picker-form, #picker-type">
|
||||||
|
{% if total_table %}
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">{% trans 'Category' %}</th>
|
||||||
|
<th scope="col">{% trans 'Income' %}</th>
|
||||||
|
<th scope="col">{% trans 'Expense' %}</th>
|
||||||
|
<th scope="col">{% trans 'Total' %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for category in total_table.values %}
|
||||||
|
<tr>
|
||||||
|
<th>{% if category.name %}{{ category.name }}{% else %}{% trans 'Uncategorized' %}{% endif %}</th>
|
||||||
|
<td>
|
||||||
|
{% for currency in category.currencies.values %}
|
||||||
|
{% if currency.total_income != 0 %}
|
||||||
|
<c-amount.display
|
||||||
|
:amount="currency.total_income"
|
||||||
|
:prefix="currency.currency.prefix"
|
||||||
|
:suffix="currency.currency.suffix"
|
||||||
|
:decimal_places="currency.currency.decimal_places"
|
||||||
|
color="green"></c-amount.display>
|
||||||
|
{% else %}
|
||||||
|
<div>-</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% for currency in category.currencies.values %}
|
||||||
|
{% if currency.total_expense != 0 %}
|
||||||
|
<c-amount.display
|
||||||
|
:amount="currency.total_expense"
|
||||||
|
:prefix="currency.currency.prefix"
|
||||||
|
:suffix="currency.currency.suffix"
|
||||||
|
:decimal_places="currency.currency.decimal_places"
|
||||||
|
color="red"></c-amount.display>
|
||||||
|
{% else %}
|
||||||
|
<div>-</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% for currency in category.currencies.values %}
|
||||||
|
{% if currency.total_final != 0 %}
|
||||||
|
<c-amount.display
|
||||||
|
:amount="currency.total_final"
|
||||||
|
:prefix="currency.currency.prefix"
|
||||||
|
:suffix="currency.currency.suffix"
|
||||||
|
:decimal_places="currency.currency.decimal_places"
|
||||||
|
color="{% if currency.total_final < 0 %}red{% else %}green{% endif %}"></c-amount.display>
|
||||||
|
{% else %}
|
||||||
|
<div>-</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<c-msg.empty title="{% translate "No categories" %}"></c-msg.empty>
|
||||||
|
{% endif %}
|
||||||
|
</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>
|
||||||
@@ -72,7 +72,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<hr class="mt-0">
|
|
||||||
<div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist"
|
<div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist"
|
||||||
aria-orientation="vertical">
|
aria-orientation="vertical">
|
||||||
<button class="nav-link" id="v-pills-tab" data-bs-toggle="pill" data-bs-target="#v-pills-content"
|
<button class="nav-link" id="v-pills-tab" data-bs-toggle="pill" data-bs-target="#v-pills-content"
|
||||||
@@ -95,6 +94,26 @@
|
|||||||
hx-indicator="#tab-content"
|
hx-indicator="#tab-content"
|
||||||
hx-target="#tab-content">{% trans 'Category Explorer' %}
|
hx-target="#tab-content">{% trans 'Category Explorer' %}
|
||||||
</button>
|
</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_overview' %}"
|
||||||
|
hx-include="#picker-form, #picker-type"
|
||||||
|
hx-indicator="#tab-content"
|
||||||
|
hx-target="#tab-content">{% trans 'Categories Overview' %}
|
||||||
|
</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>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -58,5 +58,39 @@
|
|||||||
<i class="fa-solid fa-rotate me-2"></i><span>{% trans 'Invert' %}</span>
|
<i class="fa-solid fa-rotate me-2"></i><span>{% trans 'Invert' %}</span>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -174,7 +174,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="search" class="my-3">
|
<div id="search" class="my-3">
|
||||||
<label class="w-100">
|
<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
|
_="on input or search or htmx:afterSwap from window
|
||||||
if my value is empty
|
if my value is empty
|
||||||
trigger toggle on <.transactions-divider-collapse/>
|
trigger toggle on <.transactions-divider-collapse/>
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ import AirDatepicker from 'air-datepicker';
|
|||||||
import en from 'air-datepicker/locale/en';
|
import en from 'air-datepicker/locale/en';
|
||||||
import ptBr from 'air-datepicker/locale/pt-BR';
|
import ptBr from 'air-datepicker/locale/pt-BR';
|
||||||
import nl from 'air-datepicker/locale/nl';
|
import nl from 'air-datepicker/locale/nl';
|
||||||
|
import de from 'air-datepicker/locale/de';
|
||||||
import {createPopper} from '@popperjs/core';
|
import {createPopper} from '@popperjs/core';
|
||||||
|
|
||||||
const locales = {
|
const locales = {
|
||||||
'pt': ptBr,
|
'pt': ptBr,
|
||||||
'en': en,
|
'en': en,
|
||||||
'nl': nl
|
'nl': nl,
|
||||||
|
'de': de
|
||||||
};
|
};
|
||||||
|
|
||||||
function isMobileDevice() {
|
function isMobileDevice() {
|
||||||
@@ -43,7 +45,7 @@ window.DatePicker = function createDynamicDatePicker(element) {
|
|||||||
toggleSelected: element.dataset.toggleSelected === 'true',
|
toggleSelected: element.dataset.toggleSelected === 'true',
|
||||||
autoClose: element.dataset.autoClose === 'true',
|
autoClose: element.dataset.autoClose === 'true',
|
||||||
buttons: element.dataset.clearButton === 'true' ? ['clear', todayButton] : [todayButton],
|
buttons: element.dataset.clearButton === 'true' ? ['clear', todayButton] : [todayButton],
|
||||||
locale: locales[element.dataset.language],
|
locale: locales[element.dataset.language] || locales['en'],
|
||||||
onSelect: ({date, formattedDate, datepicker}) => {
|
onSelect: ({date, formattedDate, datepicker}) => {
|
||||||
const _event = new CustomEvent("change", {
|
const _event = new CustomEvent("change", {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
@@ -117,7 +119,7 @@ window.MonthYearPicker = function createDynamicDatePicker(element) {
|
|||||||
toggleSelected: element.dataset.toggleSelected === 'true',
|
toggleSelected: element.dataset.toggleSelected === 'true',
|
||||||
autoClose: element.dataset.autoClose === 'true',
|
autoClose: element.dataset.autoClose === 'true',
|
||||||
buttons: element.dataset.clearButton === 'true' ? ['clear', todayButton] : [todayButton],
|
buttons: element.dataset.clearButton === 'true' ? ['clear', todayButton] : [todayButton],
|
||||||
locale: locales[element.dataset.language],
|
locale: locales[element.dataset.language] || locales['en'],
|
||||||
onSelect: ({date, formattedDate, datepicker}) => {
|
onSelect: ({date, formattedDate, datepicker}) => {
|
||||||
const _event = new CustomEvent("change", {
|
const _event = new CustomEvent("change", {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
@@ -190,7 +192,7 @@ window.YearPicker = function createDynamicDatePicker(element) {
|
|||||||
toggleSelected: element.dataset.toggleSelected === 'true',
|
toggleSelected: element.dataset.toggleSelected === 'true',
|
||||||
autoClose: element.dataset.autoClose === 'true',
|
autoClose: element.dataset.autoClose === 'true',
|
||||||
buttons: element.dataset.clearButton === 'true' ? ['clear', todayButton] : [todayButton],
|
buttons: element.dataset.clearButton === 'true' ? ['clear', todayButton] : [todayButton],
|
||||||
locale: locales[element.dataset.language],
|
locale: locales[element.dataset.language] || locales['en'],
|
||||||
onSelect: ({date, formattedDate, datepicker}) => {
|
onSelect: ({date, formattedDate, datepicker}) => {
|
||||||
const _event = new CustomEvent("change", {
|
const _event = new CustomEvent("change", {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user