Compare commits

...

31 Commits
0.2.0 ... 0.4.0

Author SHA1 Message Date
Herculino Trotta
011e926e02 Merge pull request #37
locale(pt-BR): update translation
2025-01-11 13:42:11 -03:00
Herculino Trotta
cd1b872b27 locale(pt-BR): update translation 2025-01-11 13:41:40 -03:00
Herculino Trotta
3791edce63 Merge pull request #36
feat(recurring-transaction): when explicitly finishing, delete any upcoming unpaid transactions
2025-01-11 13:40:28 -03:00
Herculino Trotta
2cb8100129 feat(recurring-transaction): when explicitly finishing, delete any upcoming unpaid transactions 2025-01-11 13:40:10 -03:00
Herculino Trotta
e7e4ccafb6 Merge pull request #35 from eitchtee/dev
feat(recurring-transaction): when unpause start generating transactions from today or from existing date, whichever is higher
2025-01-11 13:39:26 -03:00
Herculino Trotta
afbbf7b25d feat(recurring-transaction): when unpause start generating transactions from today or from existing date, whichever is higher 2025-01-11 13:38:51 -03:00
Herculino Trotta
1eba2b8731 Merge pull request #34 from eitchtee/dev
feat(installment-plan): don't update paid transactions amount
2025-01-11 13:37:19 -03:00
Herculino Trotta
afe366c359 feat(installment-plan): don't update paid transactions amount 2025-01-11 13:35:52 -03:00
Herculino Trotta
3ee2bebc5c Merge pull request #33
feat(recurring-transaction): update unpaid transactions info when recurring transaction is updated
2025-01-11 13:35:14 -03:00
Herculino Trotta
b951e5f069 feat(recurring-transaction): update unpaid transactions info when recurring transaction is updated 2025-01-11 13:34:49 -03:00
Herculino Trotta
4005a83a0d Merge pull request #32
fix(calculator): rounding errors
2025-01-07 16:17:00 -03:00
Herculino Trotta
f81f1d83fd fix(calculator): rounding errors 2025-01-07 16:16:26 -03:00
Herculino Trotta
7816d6c55d Merge pull request #31
fix(transactions:action-bar): rounding errors when summing (again)
2025-01-06 00:50:41 -03:00
Herculino Trotta
6e3fdae4fe fix(transactions:action-bar): rounding errors when summing (again) 2025-01-06 00:50:17 -03:00
Herculino Trotta
e2da996217 Merge pull request #30
fix(networth): chart initializing multiple times resulting in weird animation
2025-01-06 00:14:48 -03:00
Herculino Trotta
cc2e2293ed fix(networth): chart initializing multiple times resulting in weird animation 2025-01-06 00:14:15 -03:00
Herculino Trotta
7060f07ccd Merge pull request #29
feat(calculator): localize result
2025-01-06 00:14:12 -03:00
Herculino Trotta
0adb991879 feat(calculator): localize result 2025-01-06 00:13:47 -03:00
Herculino Trotta
20e03df661 Merge pull request #28
fix(transactions:action-bar): rounding errors when summing
2025-01-06 00:11:55 -03:00
Herculino Trotta
71f59bfd68 fix(transactions:action-bar): rounding errors when summing 2025-01-06 00:10:40 -03:00
Herculino Trotta
6c76535f91 Merge pull request #27
fix(transactions:action-bar): min and max calculations take into account if value is income or expense
2025-01-05 15:53:21 -03:00
Herculino Trotta
5c8fbc9278 fix(transactions:action-bar): min and max calculations take into account if value is income or expense 2025-01-05 15:52:58 -03:00
Herculino Trotta
89b11421c2 Merge pull request #26
feat(transactions:action-bar): localize calculation results
2025-01-05 15:42:51 -03:00
Herculino Trotta
056fc4fced feat(transactions:action-bar): localize calculation results 2025-01-05 15:42:28 -03:00
Herculino Trotta
3f9765ec7b Merge pull request #25
refactor(transactions:action-bar): remove debug log
2025-01-05 15:22:52 -03:00
Herculino Trotta
0d9d13bf31 refactor(transactions:action-bar): remove debug log 2025-01-05 15:22:18 -03:00
Herculino Trotta
2f6c396eaf Merge pull request #24
fix(transactions:action-bar): sum button not copying correctly
2025-01-05 15:20:24 -03:00
Herculino Trotta
d12b920e54 fix(transactions:action-bar): sum button not copying correctly 2025-01-05 15:19:58 -03:00
Herculino Trotta
9edbf7bd5a Merge pull request #23
feat(transactions:action-bar): add more math options in a dropdown
2025-01-05 14:36:07 -03:00
Herculino Trotta
dbd3eea29a locale(pt-BR): update translation 2025-01-05 14:35:33 -03:00
Herculino Trotta
881fed1895 feat(transactions:action-bar): add more math options in a dropdown 2025-01-05 14:35:23 -03:00
10 changed files with 405 additions and 144 deletions

View File

@@ -120,6 +120,11 @@ class RecurringTransactionSerializer(serializers.ModelSerializer):
instance.create_upcoming_transactions()
return instance
def update(self, instance, validated_data):
instance = super().update(instance, validated_data)
instance.update_unpaid_transactions()
return instance
class TransactionSerializer(serializers.ModelSerializer):
category = TransactionCategoryField(required=False)

View File

@@ -767,5 +767,7 @@ class RecurringTransactionForm(forms.ModelForm):
instance = super().save(**kwargs)
if is_new:
instance.create_upcoming_transactions()
else:
instance.update_unpaid_transactions()
return instance

View File

@@ -334,10 +334,15 @@ class InstallmentPlan(models.Model):
existing_transaction.type = self.type
existing_transaction.date = transaction_date
existing_transaction.reference_date = transaction_reference_date
existing_transaction.amount = self.installment_amount
existing_transaction.description = self.description
existing_transaction.category = self.category
existing_transaction.notes = self.notes
if (
not existing_transaction.is_paid
): # Don't update value for paid transactions
existing_transaction.amount = self.installment_amount
existing_transaction.save()
# Update tags
@@ -540,3 +545,33 @@ class RecurringTransaction(models.Model):
recurring_transaction.save(
update_fields=["last_generated_date", "last_generated_reference_date"]
)
def update_unpaid_transactions(self):
"""
Updates all unpaid transactions associated with this RecurringTransaction.
Only unpaid transactions (`is_paid=False`) are modified. Updates fields like
amount, description, category, notes, and many-to-many relationships (tags, entities).
"""
unpaid_transactions = self.transactions.filter(is_paid=False)
for existing_transaction in unpaid_transactions:
# Update fields based on RecurringTransaction
existing_transaction.amount = self.amount
existing_transaction.description = self.description
existing_transaction.category = self.category
existing_transaction.notes = self.notes
# Update many-to-many relationships
existing_transaction.tags.set(self.tags.all())
existing_transaction.entities.set(self.entities.all())
# Save updated transaction
existing_transaction.save()
def delete_unpaid_transactions(self):
"""
Deletes all unpaid transactions associated with this RecurringTransaction.
"""
today = timezone.localdate(timezone.now())
self.transactions.filter(is_paid=False, date__gt=today).delete()

View File

@@ -168,12 +168,26 @@ def recurring_transaction_toggle_pause(request, recurring_transaction_id):
)
current_paused = recurring_transaction.is_paused
recurring_transaction.is_paused = not current_paused
recurring_transaction.save(update_fields=["is_paused"])
if current_paused:
messages.success(request, _("Recurring transaction unpaused successfully"))
today = timezone.localdate(timezone.now())
recurring_transaction.last_generated_date = max(
recurring_transaction.last_generated_date, today
)
recurring_transaction.last_generated_reference_date = max(
recurring_transaction.last_generated_reference_date, today
)
recurring_transaction.save(
update_fields=[
"last_generated_date",
"last_generated_reference_date",
"is_paused",
]
)
generate_recurring_transactions.defer()
messages.success(request, _("Recurring transaction unpaused successfully"))
else:
recurring_transaction.save(update_fields=["is_paused"])
messages.success(request, _("Recurring transaction paused successfully"))
return HttpResponse(
@@ -188,7 +202,7 @@ def recurring_transaction_toggle_pause(request, recurring_transaction_id):
@login_required
@require_http_methods(["GET"])
def recurring_transaction_finish(request, recurring_transaction_id):
recurring_transaction = get_object_or_404(
recurring_transaction: RecurringTransaction = get_object_or_404(
RecurringTransaction, id=recurring_transaction_id
)
today = timezone.localdate(timezone.now()) - relativedelta(days=1)
@@ -197,6 +211,9 @@ def recurring_transaction_finish(request, recurring_transaction_id):
recurring_transaction.is_paused = True
recurring_transaction.save(update_fields=["end_date", "is_paused"])
# Delete all unpaid transactions associated with this RecurringTransaction
recurring_transaction.delete_unpaid_transactions()
messages.success(request, _("Recurring transaction finished successfully"))
return HttpResponse(

View File

@@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-01-04 21:19+0000\n"
"PO-Revision-Date: 2025-01-04 18:22-0300\n"
"POT-Creation-Date: 2025-01-11 16:40+0000\n"
"PO-Revision-Date: 2025-01-11 13:41-0300\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: pt_BR\n"
@@ -66,7 +66,7 @@ msgstr "Novo saldo"
#: apps/transactions/forms.py:39 apps/transactions/forms.py:209
#: apps/transactions/forms.py:216 apps/transactions/forms.py:395
#: apps/transactions/forms.py:637 apps/transactions/models.py:109
#: apps/transactions/models.py:228 apps/transactions/models.py:403
#: apps/transactions/models.py:228 apps/transactions/models.py:408
msgid "Category"
msgstr "Categoria"
@@ -75,7 +75,7 @@ msgstr "Categoria"
#: apps/transactions/forms.py:225 apps/transactions/forms.py:233
#: apps/transactions/forms.py:388 apps/transactions/forms.py:630
#: apps/transactions/models.py:115 apps/transactions/models.py:230
#: apps/transactions/models.py:407 templates/includes/navbar.html:98
#: apps/transactions/models.py:412 templates/includes/navbar.html:98
#: templates/tags/fragments/list.html:5 templates/tags/pages/index.html:4
msgid "Tags"
msgstr "Tags"
@@ -85,12 +85,13 @@ msgstr "Tags"
#: apps/transactions/models.py:39 apps/transactions/models.py:58
#: templates/account_groups/fragments/list.html:25
#: templates/accounts/fragments/list.html:25
#: templates/categories/fragments/list.html:25
#: templates/categories/fragments/table.html:16
#: templates/currencies/fragments/list.html:26
#: templates/entities/fragments/list.html:25
#: templates/entities/fragments/table.html:16
#: templates/installment_plans/fragments/table.html:16
#: templates/recurring_transactions/fragments/table.html:18
#: templates/rules/fragments/list.html:26 templates/tags/fragments/list.html:25
#: templates/rules/fragments/list.html:26
#: templates/tags/fragments/table.html:16
msgid "Name"
msgstr "Nome"
@@ -130,6 +131,9 @@ msgstr ""
"mês."
#: apps/accounts/models.py:54 templates/accounts/fragments/list.html:30
#: templates/categories/fragments/list.html:24
#: templates/entities/fragments/list.html:24
#: templates/tags/fragments/list.html:24
msgid "Archived"
msgstr "Arquivada"
@@ -141,7 +145,7 @@ msgstr ""
#: apps/accounts/models.py:59 apps/rules/models.py:19
#: apps/transactions/forms.py:59 apps/transactions/forms.py:380
#: apps/transactions/forms.py:622 apps/transactions/models.py:84
#: apps/transactions/models.py:188 apps/transactions/models.py:385
#: apps/transactions/models.py:188 apps/transactions/models.py:390
msgid "Account"
msgstr "Conta"
@@ -213,7 +217,7 @@ msgstr "Entidade com esse ID não existe."
msgid "Invalid entity data. Provide an ID or name."
msgstr "Dados da entidade inválidos. Forneça um ID ou nome."
#: apps/api/serializers/transactions.py:96
#: apps/api/serializers/transactions.py:168
msgid "Either 'date' or 'reference_date' must be provided."
msgstr "É necessário fornecer “date” ou “reference_date”."
@@ -416,7 +420,7 @@ msgstr "Moeda de pagamento"
#: apps/dca/models.py:27 apps/dca/models.py:179 apps/rules/models.py:26
#: apps/transactions/forms.py:250 apps/transactions/models.py:105
#: apps/transactions/models.py:237 apps/transactions/models.py:413
#: apps/transactions/models.py:237 apps/transactions/models.py:418
msgid "Notes"
msgstr "Notas"
@@ -513,7 +517,7 @@ msgstr "Já existe um valor para esse campo na regra."
#: apps/rules/models.py:10 apps/rules/models.py:25
#: apps/transactions/forms.py:242 apps/transactions/models.py:104
#: apps/transactions/models.py:195 apps/transactions/models.py:399
#: apps/transactions/models.py:195 apps/transactions/models.py:404
msgid "Description"
msgstr "Descrição"
@@ -522,7 +526,7 @@ msgid "Trigger"
msgstr "Gatilho"
#: apps/rules/models.py:20 apps/transactions/models.py:91
#: apps/transactions/models.py:193 apps/transactions/models.py:391
#: apps/transactions/models.py:193 apps/transactions/models.py:396
msgid "Type"
msgstr "Tipo"
@@ -534,12 +538,12 @@ msgstr "Pago"
#: apps/rules/models.py:23 apps/transactions/forms.py:62
#: apps/transactions/forms.py:241 apps/transactions/forms.py:407
#: apps/transactions/forms.py:649 apps/transactions/models.py:95
#: apps/transactions/models.py:211 apps/transactions/models.py:415
#: apps/transactions/models.py:211 apps/transactions/models.py:420
msgid "Reference Date"
msgstr "Data de Referência"
#: apps/rules/models.py:24 apps/transactions/models.py:100
#: apps/transactions/models.py:396
#: apps/transactions/models.py:401
msgid "Amount"
msgstr "Quantia"
@@ -547,7 +551,7 @@ msgstr "Quantia"
#: apps/transactions/forms.py:55 apps/transactions/forms.py:403
#: apps/transactions/forms.py:645 apps/transactions/models.py:69
#: apps/transactions/models.py:120 apps/transactions/models.py:233
#: apps/transactions/models.py:410 templates/entities/fragments/list.html:5
#: apps/transactions/models.py:415 templates/entities/fragments/list.html:5
#: templates/entities/pages/index.html:4 templates/includes/navbar.html:100
msgid "Entities"
msgstr "Entidades"
@@ -682,8 +686,10 @@ msgid "Mute"
msgstr "Silenciada"
#: apps/transactions/models.py:23 apps/transactions/models.py:42
#: apps/transactions/models.py:61
#: apps/transactions/models.py:61 templates/categories/fragments/list.html:21
#: templates/entities/fragments/list.html:21
#: templates/recurring_transactions/fragments/list.html:21
#: templates/tags/fragments/list.html:21
msgid "Active"
msgstr "Ativo"
@@ -747,7 +753,7 @@ msgstr "Despesa"
msgid "Installment Plan"
msgstr "Parcelamento"
#: apps/transactions/models.py:140 apps/transactions/models.py:436
#: apps/transactions/models.py:140 apps/transactions/models.py:441
msgid "Recurring Transaction"
msgstr "Transação Recorrente"
@@ -792,11 +798,11 @@ msgstr "Parcela inicial"
msgid "The installment number to start counting from"
msgstr "O número da parcela a partir do qual se inicia a contagem"
#: apps/transactions/models.py:209 apps/transactions/models.py:419
#: apps/transactions/models.py:209 apps/transactions/models.py:424
msgid "Start Date"
msgstr "Data de Início"
#: apps/transactions/models.py:213 apps/transactions/models.py:420
#: apps/transactions/models.py:213 apps/transactions/models.py:425
msgid "End Date"
msgstr "Data Final"
@@ -814,44 +820,44 @@ msgstr "Valor da Parcela"
msgid "Installment Plans"
msgstr "Parcelamentos"
#: apps/transactions/models.py:378
#: apps/transactions/models.py:383
msgid "day(s)"
msgstr "dia(s)"
#: apps/transactions/models.py:379
#: apps/transactions/models.py:384
msgid "week(s)"
msgstr "semana(s)"
#: apps/transactions/models.py:380
#: apps/transactions/models.py:385
msgid "month(s)"
msgstr "mês(es)"
#: apps/transactions/models.py:381
#: apps/transactions/models.py:386
msgid "year(s)"
msgstr "ano(s)"
#: apps/transactions/models.py:383
#: apps/transactions/models.py:388
#: templates/recurring_transactions/fragments/list.html:24
msgid "Paused"
msgstr "Pausado"
#: apps/transactions/models.py:422
#: apps/transactions/models.py:427
msgid "Recurrence Type"
msgstr "Tipo de recorrência"
#: apps/transactions/models.py:425
#: apps/transactions/models.py:430
msgid "Recurrence Interval"
msgstr "Intervalo de recorrência"
#: apps/transactions/models.py:429
#: apps/transactions/models.py:434
msgid "Last Generated Date"
msgstr "Última data gerada"
#: apps/transactions/models.py:432
#: apps/transactions/models.py:437
msgid "Last Generated Reference Date"
msgstr "Última data de referência gerada"
#: apps/transactions/models.py:437 templates/includes/navbar.html:64
#: apps/transactions/models.py:442 templates/includes/navbar.html:64
#: templates/recurring_transactions/fragments/list.html:5
#: templates/recurring_transactions/pages/index.html:4
msgid "Recurring Transactions"
@@ -867,27 +873,27 @@ msgstr "%(value)s tem muitas casas decimais. O máximo é 30."
msgid "%(value)s is not a non-negative number"
msgstr "%(value)s não é um número positivo"
#: apps/transactions/views/categories.py:44
#: apps/transactions/views/categories.py:66
msgid "Category added successfully"
msgstr "Categoria adicionada com sucesso"
#: apps/transactions/views/categories.py:72
#: apps/transactions/views/categories.py:94
msgid "Category updated successfully"
msgstr "Categoria atualizada com sucesso"
#: apps/transactions/views/categories.py:99
#: apps/transactions/views/categories.py:121
msgid "Category deleted successfully"
msgstr "Categoria apagada com sucesso"
#: apps/transactions/views/entities.py:43
#: apps/transactions/views/entities.py:65
msgid "Entity added successfully"
msgstr "Entidade adicionada com sucesso"
#: apps/transactions/views/entities.py:71
#: apps/transactions/views/entities.py:93
msgid "Entity updated successfully"
msgstr "Entidade atualizada com sucesso"
#: apps/transactions/views/entities.py:98
#: apps/transactions/views/entities.py:120
msgid "Entity deleted successfully"
msgstr "Entidade apagada com sucesso"
@@ -915,31 +921,31 @@ msgstr "Transação Recorrente adicionada com sucesso"
msgid "Recurring Transaction updated successfully"
msgstr "Transação Recorrente atualizada com sucesso"
#: apps/transactions/views/recurring_transactions.py:174
#: apps/transactions/views/recurring_transactions.py:188
msgid "Recurring transaction unpaused successfully"
msgstr "Transação Recorrente despausada com sucesso"
#: apps/transactions/views/recurring_transactions.py:177
#: apps/transactions/views/recurring_transactions.py:191
msgid "Recurring transaction paused successfully"
msgstr "Transação Recorrente pausada com sucesso"
#: apps/transactions/views/recurring_transactions.py:200
#: apps/transactions/views/recurring_transactions.py:217
msgid "Recurring transaction finished successfully"
msgstr "Transação Recorrente finalizada com sucesso"
#: apps/transactions/views/recurring_transactions.py:221
#: apps/transactions/views/recurring_transactions.py:238
msgid "Recurring Transaction deleted successfully"
msgstr "Transação Recorrente apagada com sucesso"
#: apps/transactions/views/tags.py:43
#: apps/transactions/views/tags.py:65
msgid "Tag added successfully"
msgstr "Tag adicionada com sucesso"
#: apps/transactions/views/tags.py:71
#: apps/transactions/views/tags.py:93
msgid "Tag updated successfully"
msgstr "Tag atualizada com sucesso"
#: apps/transactions/views/tags.py:98
#: apps/transactions/views/tags.py:120
msgid "Tag deleted successfully"
msgstr "Tag apagada com sucesso"
@@ -1065,62 +1071,63 @@ msgstr "Editar grupo de conta"
#: templates/account_groups/fragments/list.html:32
#: templates/accounts/fragments/list.html:37
#: templates/categories/fragments/list.html:33
#: templates/categories/fragments/table.html:24
#: templates/currencies/fragments/list.html:33
#: templates/dca/fragments/strategy/details.html:63
#: templates/entities/fragments/list.html:32
#: templates/entities/fragments/table.html:23
#: templates/exchange_rates/fragments/table.html:19
#: templates/installment_plans/fragments/table.html:23
#: templates/recurring_transactions/fragments/table.html:25
#: templates/rules/fragments/list.html:33 templates/tags/fragments/list.html:32
#: templates/rules/fragments/list.html:33
#: templates/tags/fragments/table.html:23
msgid "Actions"
msgstr "Ações"
#: templates/account_groups/fragments/list.html:36
#: templates/accounts/fragments/list.html:41
#: templates/categories/fragments/list.html:37
#: templates/categories/fragments/table.html:29
#: templates/cotton/transaction/item.html:109
#: templates/currencies/fragments/list.html:37
#: templates/dca/fragments/strategy/details.html:67
#: templates/dca/fragments/strategy/list.html:34
#: templates/entities/fragments/list.html:36
#: templates/entities/fragments/table.html:28
#: templates/exchange_rates/fragments/table.html:23
#: templates/installment_plans/fragments/table.html:27
#: templates/recurring_transactions/fragments/table.html:29
#: templates/rules/fragments/transaction_rule/view.html:22
#: templates/rules/fragments/transaction_rule/view.html:48
#: templates/tags/fragments/list.html:36
#: templates/tags/fragments/table.html:28
msgid "Edit"
msgstr "Editar"
#: templates/account_groups/fragments/list.html:43
#: templates/accounts/fragments/list.html:48
#: templates/categories/fragments/list.html:44
#: templates/categories/fragments/table.html:36
#: templates/cotton/transaction/item.html:116
#: templates/cotton/ui/transactions_action_bar.html:50
#: templates/currencies/fragments/list.html:44
#: templates/dca/fragments/strategy/details.html:75
#: templates/dca/fragments/strategy/list.html:42
#: templates/entities/fragments/list.html:43
#: templates/entities/fragments/table.html:36
#: templates/exchange_rates/fragments/table.html:31
#: templates/installment_plans/fragments/table.html:56
#: templates/mini_tools/unit_price_calculator.html:18
#: templates/recurring_transactions/fragments/table.html:91
#: templates/rules/fragments/list.html:44
#: templates/rules/fragments/transaction_rule/view.html:56
#: templates/tags/fragments/list.html:43
#: templates/tags/fragments/table.html:36
msgid "Delete"
msgstr "Apagar"
#: templates/account_groups/fragments/list.html:47
#: templates/accounts/fragments/list.html:52
#: templates/categories/fragments/list.html:48
#: templates/categories/fragments/table.html:41
#: templates/cotton/transaction/item.html:120
#: templates/cotton/ui/transactions_action_bar.html:52
#: templates/currencies/fragments/list.html:48
#: templates/dca/fragments/strategy/details.html:80
#: templates/dca/fragments/strategy/list.html:46
#: templates/entities/fragments/list.html:47
#: templates/entities/fragments/table.html:40
#: templates/exchange_rates/fragments/table.html:36
#: templates/installment_plans/fragments/table.html:48
#: templates/installment_plans/fragments/table.html:60
@@ -1130,40 +1137,40 @@ msgstr "Apagar"
#: templates/recurring_transactions/fragments/table.html:96
#: templates/rules/fragments/list.html:48
#: templates/rules/fragments/transaction_rule/view.html:60
#: templates/tags/fragments/list.html:47
#: templates/tags/fragments/table.html:40
msgid "Are you sure?"
msgstr "Tem certeza?"
#: templates/account_groups/fragments/list.html:48
#: templates/accounts/fragments/list.html:53
#: templates/categories/fragments/list.html:49
#: templates/categories/fragments/table.html:42
#: templates/cotton/transaction/item.html:121
#: templates/cotton/ui/transactions_action_bar.html:53
#: templates/currencies/fragments/list.html:49
#: templates/dca/fragments/strategy/details.html:81
#: templates/dca/fragments/strategy/list.html:47
#: templates/entities/fragments/list.html:48
#: templates/entities/fragments/table.html:41
#: templates/exchange_rates/fragments/table.html:37
#: templates/rules/fragments/list.html:49
#: templates/rules/fragments/transaction_rule/view.html:61
#: templates/tags/fragments/list.html:48
#: templates/tags/fragments/table.html:41
msgid "You won't be able to revert this!"
msgstr "Você não será capaz de reverter isso!"
#: templates/account_groups/fragments/list.html:49
#: templates/accounts/fragments/list.html:54
#: templates/categories/fragments/list.html:50
#: templates/categories/fragments/table.html:43
#: templates/cotton/transaction/item.html:122
#: templates/currencies/fragments/list.html:50
#: templates/dca/fragments/strategy/details.html:82
#: templates/dca/fragments/strategy/list.html:48
#: templates/entities/fragments/list.html:49
#: templates/entities/fragments/table.html:42
#: templates/exchange_rates/fragments/table.html:38
#: templates/installment_plans/fragments/table.html:62
#: templates/recurring_transactions/fragments/table.html:98
#: templates/rules/fragments/list.html:50
#: templates/rules/fragments/transaction_rule/view.html:62
#: templates/tags/fragments/list.html:49
#: templates/tags/fragments/table.html:42
msgid "Yes, delete it!"
msgstr "Sim, apague!"
@@ -1273,11 +1280,11 @@ msgstr "Adicionar categoria"
msgid "Edit category"
msgstr "Editar categoria"
#: templates/categories/fragments/list.html:26
#: templates/categories/fragments/table.html:17
msgid "Muted"
msgstr "Silenciada"
#: templates/categories/fragments/list.html:63
#: templates/categories/fragments/table.html:57
msgid "No categories"
msgstr "Nenhum categoria"
@@ -1337,10 +1344,44 @@ msgstr "Marcar como não pago"
msgid "Yes, delete them!"
msgstr "Sim, apague!"
#: templates/cotton/ui/transactions_action_bar.html:81
#: templates/cotton/ui/transactions_action_bar.html:101
#: templates/cotton/ui/transactions_action_bar.html:125
#: templates/cotton/ui/transactions_action_bar.html:145
#: templates/cotton/ui/transactions_action_bar.html:165
#: templates/cotton/ui/transactions_action_bar.html:185
#: templates/cotton/ui/transactions_action_bar.html:205
#: templates/cotton/ui/transactions_action_bar.html:225
msgid "copied!"
msgstr "copiado!"
#: templates/cotton/ui/transactions_action_bar.html:110
msgid "Toggle Dropdown"
msgstr "Alternar menu suspenso"
#: templates/cotton/ui/transactions_action_bar.html:118
msgid "Flat Total"
msgstr "Total Fixo"
#: templates/cotton/ui/transactions_action_bar.html:138
msgid "Real Total"
msgstr "Total Real"
#: templates/cotton/ui/transactions_action_bar.html:158
msgid "Mean"
msgstr "Média"
#: templates/cotton/ui/transactions_action_bar.html:178
msgid "Max"
msgstr "Máximo"
#: templates/cotton/ui/transactions_action_bar.html:198
msgid "Min"
msgstr "Minímo"
#: templates/cotton/ui/transactions_action_bar.html:218
msgid "Count"
msgstr "Contagem"
#: templates/currencies/fragments/add.html:5
msgid "Add currency"
msgstr "Adicionar moeda"
@@ -1479,7 +1520,7 @@ msgstr "Adicionar entidade"
msgid "Edit entity"
msgstr "Editar entidade"
#: templates/entities/fragments/list.html:59
#: templates/entities/fragments/table.html:53
msgid "No entities"
msgstr "Sem entidades"
@@ -1800,8 +1841,12 @@ msgid "Finish"
msgstr "Finalizar"
#: templates/recurring_transactions/fragments/table.html:83
msgid "This will stop the creation of new transactions"
msgstr "Isso interromperá a criação de novas transações"
msgid ""
"This will stop the creation of new transactions and delete any unpaid "
"transactions after today"
msgstr ""
"Isso interromperá a criação de novas transações e apagará transações não "
"pagas depois de hoje"
#: templates/recurring_transactions/fragments/table.html:84
msgid "Yes, finish it!"
@@ -1884,7 +1929,7 @@ msgstr "Adicionar tag"
msgid "Edit tag"
msgstr "Editar tag"
#: templates/tags/fragments/list.html:59
#: templates/tags/fragments/table.html:53
msgid "No tags"
msgstr "Nenhuma tag"
@@ -1987,6 +2032,9 @@ msgstr "Visão Anual"
msgid "Year"
msgstr "Ano"
#~ msgid "This will stop the creation of new transactions"
#~ msgstr "Isso interromperá a criação de novas transações"
#~ msgid "Is an asset account?"
#~ msgstr "É uma conta de ativos?"

View File

@@ -11,78 +11,228 @@
<div class="card slide-in-left">
<div class="card-body p-2">
{% spaceless %}
<div class="btn-group" role="group">
<div class="btn-group" role="group">
<button class="btn btn-secondary btn-sm"
data-bs-toggle="tooltip"
data-bs-title="{% translate 'Select All' %}"
_="on click set <#transactions-list input[type='checkbox']/>'s checked to true then call me.blur() then trigger change">
<i class="fa-regular fa-square-check tw-text-green-400"></i>
</button>
<button class="btn btn-secondary btn-sm"
data-bs-toggle="tooltip"
data-bs-title="{% translate 'Unselect All' %}"
_="on click set <#transactions-list input[type='checkbox']/>'s checked to false then call me.blur() then trigger change">
<i class="fa-regular fa-square tw-text-red-400"></i>
</button>
</div>
<div class="vr mx-3 tw-align-middle"></div>
<div class="btn-group me-3" role="group">
<button class="btn btn-secondary btn-sm"
hx-get="{% url 'transactions_bulk_pay' %}"
hx-include=".transaction"
data-bs-toggle="tooltip"
data-bs-title="{% translate 'Mark as paid' %}">
<i class="fa-regular fa-circle-check tw-text-green-400"></i>
</button>
<button class="btn btn-secondary btn-sm"
hx-get="{% url 'transactions_bulk_unpay' %}"
hx-include=".transaction"
data-bs-toggle="tooltip"
data-bs-title="{% translate 'Mark as unpaid' %}">
<i class="fa-regular fa-circle tw-text-red-400"></i>
</button>
</div>
<button class="btn btn-secondary btn-sm"
data-bs-toggle="tooltip"
data-bs-title="{% translate 'Select All' %}"
_="on click set <#transactions-list input[type='checkbox']/>'s checked to true then call me.blur() then trigger change">
<i class="fa-regular fa-square-check tw-text-green-400"></i>
</button>
<button class="btn btn-secondary btn-sm"
data-bs-toggle="tooltip"
data-bs-title="{% translate 'Unselect All' %}"
_="on click set <#transactions-list input[type='checkbox']/>'s checked to false then call me.blur() then trigger change">
<i class="fa-regular fa-square tw-text-red-400"></i>
</button>
</div>
<div class="vr mx-3 tw-align-middle"></div>
<div class="btn-group me-3" role="group">
<button class="btn btn-secondary btn-sm"
hx-get="{% url 'transactions_bulk_pay' %}"
hx-include=".transaction"
data-bs-toggle="tooltip"
data-bs-title="{% translate 'Mark as paid' %}">
<i class="fa-regular fa-circle-check tw-text-green-400"></i>
</button>
<button class="btn btn-secondary btn-sm"
hx-get="{% url 'transactions_bulk_unpay' %}"
hx-get="{% url 'transactions_bulk_delete' %}"
hx-include=".transaction"
hx-trigger="confirmed"
data-bs-toggle="tooltip"
data-bs-title="{% translate 'Mark as unpaid' %}">
<i class="fa-regular fa-circle tw-text-red-400"></i>
data-bs-title="{% translate 'Delete' %}"
data-bypass-on-ctrl="true"
data-title="{% translate "Are you sure?" %}"
data-text="{% translate "You won't be able to revert this!" %}"
data-confirm-text="{% translate "Yes, delete them!" %}"
_="install prompt_swal">
<i class="fa-solid fa-trash text-danger"></i>
</button>
</div>
<button class="btn btn-secondary btn-sm"
hx-get="{% url 'transactions_bulk_delete' %}"
hx-include=".transaction"
hx-trigger="confirmed"
data-bs-toggle="tooltip"
data-bs-title="{% translate 'Delete' %}"
data-bypass-on-ctrl="true"
data-title="{% translate "Are you sure?" %}"
data-text="{% translate "You won't be able to revert this!" %}"
data-confirm-text="{% translate "Yes, delete them!" %}"
_="install prompt_swal">
<i class="fa-solid fa-trash text-danger"></i>
</button>
<div class="vr mx-3 tw-align-middle"></div>
<span _="on selected_transactions_updated from #actions-bar
set realTotal to 0.0
set flatTotal to 0.0
for transaction in <.transaction:has(input[name='transactions']:checked)/>
set amt to first <.main-amount .amount/> in transaction
set amountValue to parseFloat(amt.getAttribute('data-amount'))
if not isNaN(amountValue)
set flatTotal to flatTotal + (amountValue * 100)
<div class="vr mx-3 tw-align-middle"></div>
<div class="btn-group"
_="on selected_transactions_updated from #actions-bar
set realTotal to math.bignumber(0)
set flatTotal to math.bignumber(0)
set transactions to <.transaction:has(input[name='transactions']:checked)/>
set flatAmountValues to []
set realAmountValues to []
if transaction match .income
set realTotal to realTotal + (amountValue * 100)
else
set realTotal to realTotal - (amountValue * 100)
for transaction in transactions
set amt to first <.main-amount .amount/> in transaction
set amountValue to parseFloat(amt.getAttribute('data-amount'))
append amountValue to flatAmountValues
if not isNaN(amountValue)
set flatTotal to math.chain(flatTotal).add(amountValue)
if transaction match .income
append amountValue to realAmountValues
set realTotal to math.chain(realTotal).add(amountValue)
else
append -amountValue to realAmountValues
set realTotal to math.chain(realTotal).subtract(amountValue)
end
end
end
end
set realTotal to realTotal / 100
put realTotal.toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 40}) into me
end
on click
set original_value to my innerText
writeText(my innerText) on navigator.clipboard
put '{% translate "copied!" %}' into me
wait 1s
put original_value into me
end"
class="" role="button"></span>
set mean to flatTotal.divide(flatAmountValues.length).done().toNumber()
set realTotal to realTotal.done().toNumber()
set flatTotal to flatTotal.done().toNumber()
put realTotal.toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 40}) into #real-total-front's innerText
put realTotal.toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 40}) into #calc-menu-real-total's innerText
put flatTotal.toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 40}) into #calc-menu-flat-total's innerText
put Math.max.apply(Math, realAmountValues).toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 40}) into #calc-menu-max's innerText
put Math.min.apply(Math, realAmountValues).toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 40}) into #calc-menu-min's innerText
put mean.toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 40}) into #calc-menu-mean's innerText
put flatAmountValues.length.toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 40}) into #calc-menu-count's innerText
end"
>
<button class="btn btn-secondary btn-sm" _="on click
set original_value to #real-total-front's innerText
writeText(original_value) on navigator.clipboard
put '{% translate "copied!" %}' into #real-total-front's innerText
wait 1s
put original_value into #real-total-front's innerText
end">
<i class="fa-solid fa-plus fa-fw me-2 text-primary"></i>
<span id="real-total-front">0</span>
</button>
<button type="button" class="btn btn-sm btn-secondary dropdown-toggle dropdown-toggle-split"
data-bs-toggle="dropdown" aria-expanded="false" data-bs-auto-close="outside">
<span class="visually-hidden">{% trans "Toggle Dropdown" %}</span>
</button>
<ul class="dropdown-menu">
<li>
<div class="dropdown-item-text p-0">
<div>
<div class="text-body-secondary tw-text-xs tw-font-medium px-3">
{% trans "Flat Total" %}
</div>
<div class="dropdown-item px-3 tw-cursor-pointer"
id="calc-menu-flat-total"
_="on click
set original_value to my innerText
writeText(my innerText) on navigator.clipboard
put '{% translate "copied!" %}' into me
wait 1s
put original_value into me
end">
0
</div>
</div>
</div>
</li>
<li>
<div class="dropdown-item-text p-0">
<div>
<div class="text-body-secondary tw-text-xs tw-font-medium px-3">
{% trans "Real Total" %}
</div>
<div class="dropdown-item px-3 tw-cursor-pointer"
id="calc-menu-real-total"
_="on click
set original_value to my innerText
writeText(my innerText) on navigator.clipboard
put '{% translate "copied!" %}' into me
wait 1s
put original_value into me
end">
0
</div>
</div>
</div>
</li>
<li>
<div class="dropdown-item-text p-0">
<div>
<div class="text-body-secondary tw-text-xs tw-font-medium px-3">
{% trans "Mean" %}
</div>
<div class="dropdown-item px-3 tw-cursor-pointer"
id="calc-menu-mean"
_="on click
set original_value to my innerText
writeText(my innerText) on navigator.clipboard
put '{% translate "copied!" %}' into me
wait 1s
put original_value into me
end">
0
</div>
</div>
</div>
</li>
<li>
<div class="dropdown-item-text p-0">
<div>
<div class="text-body-secondary tw-text-xs tw-font-medium px-3">
{% trans "Max" %}
</div>
<div class="dropdown-item px-3 tw-cursor-pointer"
id="calc-menu-max"
_="on click
set original_value to my innerText
writeText(my innerText) on navigator.clipboard
put '{% translate "copied!" %}' into me
wait 1s
put original_value into me
end">
0
</div>
</div>
</div>
</li>
<li>
<div class="dropdown-item-text p-0">
<div>
<div class="text-body-secondary tw-text-xs tw-font-medium px-3">
{% trans "Min" %}
</div>
<div class="dropdown-item px-3 tw-cursor-pointer"
id="calc-menu-min"
_="on click
set original_value to my innerText
writeText(my innerText) on navigator.clipboard
put '{% translate "copied!" %}' into me
wait 1s
put original_value into me
end">
0
</div>
</div>
</div>
</li>
<li>
<div class="dropdown-item-text p-0">
<div>
<div class="text-body-secondary tw-text-xs tw-font-medium px-3">
{% trans "Count" %}
</div>
<div class="dropdown-item px-3 tw-cursor-pointer"
id="calc-menu-count"
_="on click
set original_value to my innerText
writeText(my innerText) on navigator.clipboard
put '{% translate "copied!" %}' into me
wait 1s
put original_value into me
end">
0
</div>
</div>
</div>
</li>
</ul>
</div>
{% endspaceless %}
</div>
</div>

View File

@@ -66,11 +66,10 @@
})
end
then set expr to it
then call math.evaluate(expr)
then call math.evaluate(expr).toNumber()
if result exists and result is a Number
js(result)
return result.toString().replace(new RegExp(',|\\.', 'g'),
match => match === '.' ? window.decimalSeparator : window.argSeparator)
return result.toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 40})
end
then set localizedResult to it
set #calculator-result.innerText to localizedResult

View File

@@ -9,7 +9,7 @@
{% block title %}{% if type == "current" %}{% translate 'Current Net Worth' %}{% else %}{% translate 'Projected Net Worth' %}{% endif %}{% endblock %}
{% block content %}
<div class="container px-md-3 py-3" _="on load call initializeAccountChart() then initializeCurrencyChart()">
<div class="container px-md-3 py-3" _="init call initializeAccountChart() then initializeCurrencyChart() end">
<div class="row gx-xl-4 gy-3 mb-4">
<div class="col-12 col-xl-5">
<div class="row row-cols-1 g-4">

View File

@@ -80,7 +80,7 @@
hx-swap="innerHTML"
data-bypass-on-ctrl="true"
data-title="{% translate "Are you sure?" %}"
data-text="{% translate "This will stop the creation of new transactions" %}"
data-text="{% translate "This will stop the creation of new transactions and delete any unpaid transactions after today" %}"
data-confirm-text="{% translate "Yes, finish it!" %}"
_="install prompt_swal">
<i class="fa-solid fa-flag-checkered fa-fw"></i></a>

View File

@@ -2,18 +2,23 @@ import _hyperscript from 'hyperscript.org/dist/_hyperscript.min';
import './_htmx.js';
import Alpine from "alpinejs";
import mask from '@alpinejs/mask';
import { create, all } from 'mathjs';
import {create, all} from 'mathjs';
window.Alpine = Alpine;
window._hyperscript = _hyperscript;
window.math = create(all, { });
window.math = create(all, {
number: 'BigNumber', // Default type of number:
// 'number' (default), 'BigNumber', or 'Fraction'
precision: 64, // Number of significant digits for BigNumbers
relTol: 1e-60,
absTol: 1e-63
});
Alpine.plugin(mask);
Alpine.start();
_hyperscript.browserInit();
const successAudio = new Audio("/static/sounds/success.mp3");
const popAudio = new Audio("/static/sounds/pop.mp3");
window.paidSound = successAudio;