Compare commits

..

6 Commits

Author SHA1 Message Date
github-actions
a60cb366b2 Auto commit for release 'develop' on 2025-01-03 2025-01-03 07:12:13 +01:00
github-actions
58e2ef187d Auto commit for release 'develop' on 2025-01-03 2025-01-03 07:01:02 +01:00
James Cole
e878d5ce07 Fix linking piggy banks to transactions. 2025-01-02 04:54:29 +01:00
github-actions
f727a38b69 Auto commit for release 'develop' on 2025-01-01 2025-01-01 16:47:50 +01:00
James Cole
c11a5384da Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2025-01-01 16:43:44 +01:00
James Cole
ed92cbd4b8 Fix piggy bank rule action 2025-01-01 16:43:31 +01:00
12 changed files with 222 additions and 169 deletions

View File

@@ -734,33 +734,33 @@
},
{
"name": "react/child-process",
"version": "v0.6.5",
"version": "v0.6.6",
"source": {
"type": "git",
"url": "https://github.com/reactphp/child-process.git",
"reference": "e71eb1aa55f057c7a4a0d08d06b0b0a484bead43"
"reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/reactphp/child-process/zipball/e71eb1aa55f057c7a4a0d08d06b0b0a484bead43",
"reference": "e71eb1aa55f057c7a4a0d08d06b0b0a484bead43",
"url": "https://api.github.com/repos/reactphp/child-process/zipball/1721e2b93d89b745664353b9cfc8f155ba8a6159",
"reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159",
"shasum": ""
},
"require": {
"evenement/evenement": "^3.0 || ^2.0 || ^1.0",
"php": ">=5.3.0",
"react/event-loop": "^1.2",
"react/stream": "^1.2"
"react/stream": "^1.4"
},
"require-dev": {
"phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35",
"react/socket": "^1.8",
"phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
"react/socket": "^1.16",
"sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0"
},
"type": "library",
"autoload": {
"psr-4": {
"React\\ChildProcess\\": "src"
"React\\ChildProcess\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -797,19 +797,15 @@
],
"support": {
"issues": "https://github.com/reactphp/child-process/issues",
"source": "https://github.com/reactphp/child-process/tree/v0.6.5"
"source": "https://github.com/reactphp/child-process/tree/v0.6.6"
},
"funding": [
{
"url": "https://github.com/WyriHaximus",
"type": "github"
},
{
"url": "https://github.com/clue",
"type": "github"
"url": "https://opencollective.com/reactphp",
"type": "open_collective"
}
],
"time": "2022-09-16T13:41:56+00:00"
"time": "2025-01-01T16:37:48+00:00"
},
{
"name": "react/dns",

View File

@@ -154,20 +154,31 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
/** @var Transaction $destination */
$destination = $journal->transactions()->with(['account'])->where('amount', '>', 0)->first();
$hits = 0;
foreach ($piggyBank->accounts as $account) {
// matches source, which means amount will be removed from piggy:
if ($source->account_id === $piggyBank->account_id) {
$operator = 'negative';
$currency = $accountRepos->getAccountCurrency($source->account) ?? $defaultCurrency;
app('log')->debug(sprintf('Currency will draw money out of piggy bank. Source currency is %s', $currency->code));
// matches source, which means amount will be removed from piggy:
if ($account->id === $source->account_id) {
$operator = 'negative';
$currency = $accountRepos->getAccountCurrency($source->account) ?? $defaultCurrency;
app('log')->debug(sprintf('Currency will draw money out of piggy bank. Source currency is %s', $currency->code));
++$hits;
}
// matches destination, which means amount will be added to piggy.
if ($account->id === $destination->account_id) {
$operator = 'positive';
$currency = $accountRepos->getAccountCurrency($destination->account) ?? $defaultCurrency;
app('log')->debug(sprintf('Currency will add money to piggy bank. Destination currency is %s', $currency->code));
++$hits;
}
}
if ($hits > 1) {
app('log')->debug(sprintf('Transaction journal is related to %d of the accounts, cannot determine what to do. Return "0".', $hits));
return '0';
}
// matches destination, which means amount will be added to piggy.
if ($destination->account_id === $piggyBank->account_id) {
$operator = 'positive';
$currency = $accountRepos->getAccountCurrency($destination->account) ?? $defaultCurrency;
app('log')->debug(sprintf('Currency will add money to piggy bank. Destination currency is %s', $currency->code));
}
if (null === $operator || null === $currency) {
app('log')->debug('Currency is NULL and operator is NULL, return "0".');

View File

@@ -80,19 +80,28 @@ class BelongsUser implements ValidationRule
private function validatePiggyBankId(int $value): bool
{
$count = PiggyBank::leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')
$count = PiggyBank::leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id')
->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id')
->where('piggy_banks.id', '=', $value)
->where('accounts.user_id', '=', auth()->user()->id)->count()
;
return 1 === $count;
return $count > 0;
}
private function validatePiggyBankName(string $value): bool
{
$count = $this->countField(PiggyBank::class, 'name', $value);
$count = PiggyBank::leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id')
->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id')
->where('piggy_banks.name', '=', $value)
->where('accounts.user_id', '=', auth()->user()->id)->count()
;
return 1 === $count;
return $count > 0;
}
protected function countField(string $class, string $field, string $value): int

View File

@@ -26,13 +26,14 @@ namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\User;
use Illuminate\Support\Facades\Log;
/**
* Class UpdatePiggybank
@@ -53,7 +54,7 @@ class UpdatePiggybank implements ActionInterface
{
$actionValue = $this->action->getValue($journal);
app('log')->debug(sprintf('Triggered rule action UpdatePiggybank on journal #%d', $journal['transaction_journal_id']));
Log::debug(sprintf('Triggered rule action UpdatePiggybank on journal #%d', $journal['transaction_journal_id']));
// refresh the transaction type.
/** @var User $user */
@@ -64,7 +65,7 @@ class UpdatePiggybank implements ActionInterface
$piggyBank = $this->findPiggyBank($user, $actionValue);
if (null === $piggyBank) {
app('log')->info(
Log::info(
sprintf('No piggy bank named "%s", cant execute action #%d of rule #%d', $actionValue, $this->action->id, $this->action->rule_id)
);
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_piggy', ['name' => $actionValue])));
@@ -72,20 +73,19 @@ class UpdatePiggybank implements ActionInterface
return false;
}
app('log')->debug(sprintf('Found piggy bank #%d ("%s")', $piggyBank->id, $piggyBank->name));
/** @var Transaction $source */
$source = $journalObj->transactions()->where('amount', '<', 0)->first();
Log::debug(sprintf('Found piggy bank #%d ("%s")', $piggyBank->id, $piggyBank->name));
/** @var Transaction $destination */
$destination = $journalObj->transactions()->where('amount', '>', 0)->first();
if ($source->account_id === $piggyBank->account_id) {
app('log')->debug('Piggy bank account is linked to source, so remove amount from piggy bank.');
throw new FireflyException('Reference the correct account here.');
$this->removeAmount($piggyBank, $journal, $journalObj, $destination->amount);
$accounts = $this->getAccounts($journalObj);
Log::debug(sprintf('Source account is #%d: "%s"', $accounts['source']->id, $accounts['source']->name));
Log::debug(sprintf('Destination account is #%d: "%s"', $accounts['destination']->id, $accounts['source']->name));
// if connected to source but not to destination, needs to be removed from source account connected to piggy bank.
if ($this->isConnected($piggyBank, $accounts['source']) && !$this->isConnected($piggyBank, $accounts['destination'])) {
Log::debug('Piggy bank account is linked to source, so remove amount from piggy bank.');
$this->removeAmount($piggyBank, $journal, $journalObj, $accounts['source'], $destination->amount);
event(
new TriggeredAuditLog(
$this->action->rule,
@@ -103,9 +103,11 @@ class UpdatePiggybank implements ActionInterface
return true;
}
if ($destination->account_id === $piggyBank->account_id) {
app('log')->debug('Piggy bank account is linked to source, so add amount to piggy bank.');
$this->addAmount($piggyBank, $journal, $journalObj, $destination->amount);
// if connected to destination but not to source, needs to be removed from source account connected to piggy bank.
if (!$this->isConnected($piggyBank, $accounts['source']) && $this->isConnected($piggyBank, $accounts['destination'])) {
Log::debug('Piggy bank account is linked to source, so add amount to piggy bank.');
$this->addAmount($piggyBank, $journal, $journalObj, $accounts['destination'], $destination->amount);
event(
new TriggeredAuditLog(
@@ -125,13 +127,13 @@ class UpdatePiggybank implements ActionInterface
return true;
}
app('log')->info(
sprintf(
'Piggy bank is not linked to source ("#%d") or destination ("#%d"), so no action will be taken.',
$source->account_id,
$destination->account_id
)
);
if ($this->isConnected($piggyBank, $accounts['source']) && $this->isConnected($piggyBank, $accounts['destination'])) {
Log::info(sprintf('Piggy bank is linked to BOTH source ("#%d") and destination ("#%d"), so no action will be taken.', $accounts['source']->id, $accounts['destination']->id));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_link_piggy', ['name' => $actionValue])));
return false;
}
Log::info(sprintf('Piggy bank is not linked to source ("#%d") or destination ("#%d"), so no action will be taken.', $accounts['source']->id, $accounts['destination']->id));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_link_piggy', ['name' => $actionValue])));
return false;
@@ -139,80 +141,102 @@ class UpdatePiggybank implements ActionInterface
private function findPiggyBank(User $user, string $name): ?PiggyBank
{
return $user->piggyBanks()->where('piggy_banks.name', $name)->first();
/** @var PiggyBankRepositoryInterface $repository */
$repository = app(PiggyBankRepositoryInterface::class);
$repository->setUser($user);
return $repository->findByName($name);
}
private function removeAmount(PiggyBank $piggyBank, array $array, TransactionJournal $journal, string $amount): void
private function removeAmount(PiggyBank $piggyBank, array $array, TransactionJournal $journal, Account $account, string $amount): void
{
$repository = app(PiggyBankRepositoryInterface::class);
$repository->setUser($journal->user);
// how much can we remove from this piggy bank?
$toRemove = $repository->getCurrentAmount($piggyBank);
app('log')->debug(sprintf('Amount is %s, max to remove is %s', $amount, $toRemove));
$toRemove = $repository->getCurrentAmount($piggyBank, $account);
Log::debug(sprintf('Amount is %s, max to remove is %s', $amount, $toRemove));
// if $amount is bigger than $toRemove, shrink it.
$amount = -1 === bccomp($amount, $toRemove) ? $amount : $toRemove;
app('log')->debug(sprintf('Amount is now %s', $amount));
Log::debug(sprintf('Amount is now %s', $amount));
// if amount is zero, stop.
if (0 === bccomp('0', $amount)) {
app('log')->warning('Amount left is zero, stop.');
Log::warning('Amount left is zero, stop.');
event(new RuleActionFailedOnArray($this->action, $array, trans('rules.cannot_remove_zero_piggy', ['name' => $piggyBank->name])));
return;
}
// make sure we can remove amount:
throw new FireflyException('Reference the correct account here.');
if (false === $repository->canRemoveAmount($piggyBank, $amount)) {
app('log')->warning(sprintf('Cannot remove %s from piggy bank.', $amount));
if (false === $repository->canRemoveAmount($piggyBank, $account, $amount)) {
Log::warning(sprintf('Cannot remove %s from piggy bank.', $amount));
event(new RuleActionFailedOnArray($this->action, $array, trans('rules.cannot_remove_from_piggy', ['amount' => $amount, 'name' => $piggyBank->name])));
return;
}
app('log')->debug(sprintf('Will now remove %s from piggy bank.', $amount));
Log::debug(sprintf('Will now remove %s from piggy bank.', $amount));
throw new FireflyException('Reference the correct account here.');
$repository->removeAmount($piggyBank, $amount, $journal);
$repository->removeAmount($piggyBank, $account, $amount, $journal);
}
private function addAmount(PiggyBank $piggyBank, array $array, TransactionJournal $journal, string $amount): void
private function addAmount(PiggyBank $piggyBank, array $array, TransactionJournal $journal, Account $account, string $amount): void
{
$repository = app(PiggyBankRepositoryInterface::class);
$repository->setUser($journal->user);
// how much can we add to the piggy bank?
if (0 !== bccomp($piggyBank->target_amount, '0')) {
$toAdd = bcsub($piggyBank->target_amount, $repository->getCurrentAmount($piggyBank));
app('log')->debug(sprintf('Max amount to add to piggy bank is %s, amount is %s', $toAdd, $amount));
$toAdd = bcsub($piggyBank->target_amount, $repository->getCurrentAmount($piggyBank, $account));
Log::debug(sprintf('Max amount to add to piggy bank is %s, amount is %s', $toAdd, $amount));
// update amount to fit:
$amount = -1 === bccomp($amount, $toAdd) ? $amount : $toAdd;
app('log')->debug(sprintf('Amount is now %s', $amount));
Log::debug(sprintf('Amount is now %s', $amount));
}
if (0 === bccomp($piggyBank->target_amount, '0')) {
app('log')->debug('Target amount is zero, can add anything.');
Log::debug('Target amount is zero, can add anything.');
}
// if amount is zero, stop.
if (0 === bccomp('0', $amount)) {
app('log')->warning('Amount left is zero, stop.');
Log::warning('Amount left is zero, stop.');
event(new RuleActionFailedOnArray($this->action, $array, trans('rules.cannot_add_zero_piggy', ['name' => $piggyBank->name])));
return;
}
// make sure we can add amount:
throw new FireflyException('Reference the correct account here.');
if (false === $repository->canAddAmount($piggyBank, $amount)) {
app('log')->warning(sprintf('Cannot add %s to piggy bank.', $amount));
if (false === $repository->canAddAmount($piggyBank, $account, $amount)) {
Log::warning(sprintf('Cannot add %s to piggy bank.', $amount));
event(new RuleActionFailedOnArray($this->action, $array, trans('rules.cannot_add_to_piggy', ['amount' => $amount, 'name' => $piggyBank->name])));
return;
}
app('log')->debug(sprintf('Will now add %s to piggy bank.', $amount));
Log::debug(sprintf('Will now add %s to piggy bank.', $amount));
$repository->addAmount($piggyBank, $amount, $journal);
$repository->addAmount($piggyBank, $account, $amount, $journal);
}
private function getAccounts(TransactionJournal $journal): array
{
return [
'source' => $journal->transactions()->where('amount', '<', '0')->first()?->account,
'destination' => $journal->transactions()->where('amount', '>', '0')->first()?->account,
];
}
private function isConnected(PiggyBank $piggyBank, ?Account $link): bool
{
if (null === $link) {
return false;
}
foreach ($piggyBank->accounts as $account) {
if ($account->id === $link->id) {
return true;
}
}
Log::debug(sprintf('Piggy bank is not connected to account #%d "%s"', $account->id, $account->name));
return false;
}
}

11
composer.lock generated
View File

@@ -1874,16 +1874,16 @@
},
{
"name": "laravel/framework",
"version": "v11.36.1",
"version": "v11.37.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "df06f5163f4550641fdf349ebc04916a61135a64"
"reference": "6cb103d2024b087eae207654b3f4b26646119ba5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/df06f5163f4550641fdf349ebc04916a61135a64",
"reference": "df06f5163f4550641fdf349ebc04916a61135a64",
"url": "https://api.github.com/repos/laravel/framework/zipball/6cb103d2024b087eae207654b3f4b26646119ba5",
"reference": "6cb103d2024b087eae207654b3f4b26646119ba5",
"shasum": ""
},
"require": {
@@ -1933,7 +1933,6 @@
"voku/portable-ascii": "^2.0.2"
},
"conflict": {
"mockery/mockery": "1.6.8",
"tightenco/collect": "<5.5.33"
},
"provide": {
@@ -2085,7 +2084,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2024-12-17T22:32:08+00:00"
"time": "2025-01-02T20:10:21+00:00"
},
{
"name": "laravel/passport",

View File

@@ -81,7 +81,7 @@ return [
'running_balance_column' => env('USE_RUNNING_BALANCE', false),
// see cer.php for exchange rates feature flag.
],
'version' => 'develop/2025-01-01',
'version' => 'develop/2025-01-03',
'api_version' => '2.1.0', // field is no longer used.
'db_version' => 25,

36
package-lock.json generated
View File

@@ -3133,9 +3133,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "22.10.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.3.tgz",
"integrity": "sha512-DifAyw4BkrufCILvD3ucnuN8eydUfc/C1GlyrnI+LK6543w5/L3VeVgf05o3B4fqSXP1dKYLOZsKfutpxPzZrw==",
"version": "22.10.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.4.tgz",
"integrity": "sha512-99l6wv4HEzBQhvaU/UGoeBoCK61SCROQaCCGyQSgX2tEQ3rKkNZ2S7CEWnS/4s1LV+8ODdK21UeyR1fHP2mXug==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6450,22 +6450,22 @@
}
},
"node_modules/get-intrinsic": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz",
"integrity": "sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==",
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz",
"integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"dunder-proto": "^1.0.0",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.0.0",
"function-bind": "^1.1.2",
"get-proto": "^1.0.0",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.0.0"
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
@@ -6474,6 +6474,20 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"dev": true,
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/get-stream": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
@@ -11273,9 +11287,9 @@
}
},
"node_modules/vite": {
"version": "6.0.6",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.0.6.tgz",
"integrity": "sha512-NSjmUuckPmDU18bHz7QZ+bTYhRR0iA72cs2QAxCqDpafJ0S6qetco0LB3WW2OxlMHS0JmAv+yZ/R3uPmMyGTjQ==",
"version": "6.0.7",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.0.7.tgz",
"integrity": "sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ==",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@@ -101,7 +101,7 @@
"webhook_trigger_UPDATE_TRANSACTION": "After transaction update",
"webhook_trigger_DESTROY_TRANSACTION": "After transaction delete",
"webhook_response_TRANSACTIONS": "Transaction details",
"webhook_response_ACCOUNTS": "Account details",
"webhook_response_ACCOUNTS": "Tilin tiedot",
"webhook_response_none_NONE": "No details",
"webhook_delivery_JSON": "JSON",
"actions": "Toiminnot",

View File

@@ -21,7 +21,7 @@
"apply_rules_checkbox": "Regels toepassen",
"fire_webhooks_checkbox": "Webhooks starten",
"no_budget_pointer": "Je hebt nog geen budgetten. Maak er een aantal op de <a href=\"budgets\">budgetten<\/a>-pagina. Met budgetten kan je je uitgaven beter bijhouden.",
"no_bill_pointer": "You seem to have no subscription yet. You should create some on the <a href=\"subscriptions\">subscription<\/a>-page. Subscriptions can help you keep track of expenses.",
"no_bill_pointer": "Je hebt nog geen abonnementen. Maak er een aantal op de <a href=\"subscriptions\">abonnementenpagina<\/a>. Met abonnementen kan je uitgaven bijhouden.",
"source_account": "Bronrekening",
"hidden_fields_preferences": "Je kan meer transactieopties inschakelen in je <a href=\"preferences\">instellingen<\/a>.",
"destination_account": "Doelrekening",
@@ -36,7 +36,7 @@
"is_reconciled_fields_dropped": "Omdat deze transactie al is afgestemd, kan je het bedrag noch de rekeningen wijzigen.",
"tags": "Tags",
"no_budget": "(geen budget)",
"no_bill": "(no subscription)",
"no_bill": "(geen abonnement)",
"category": "Categorie",
"attachments": "Bijlagen",
"notes": "Notities",
@@ -52,7 +52,7 @@
"destination_account_reconciliation": "Je kan de doelrekening van een afstemming niet wijzigen.",
"source_account_reconciliation": "Je kan de bronrekening van een afstemming niet wijzigen.",
"budget": "Budget",
"bill": "Subscription",
"bill": "Abonnement",
"you_create_withdrawal": "Je maakt een uitgave.",
"you_create_transfer": "Je maakt een overschrijving.",
"you_create_deposit": "Je maakt inkomsten.",
@@ -130,15 +130,15 @@
"response": "Reactie",
"visit_webhook_url": "Bezoek URL van webhook",
"reset_webhook_secret": "Reset webhook-geheim",
"header_exchange_rates": "Exchange rates",
"exchange_rates_intro": "Firefly III supports downloading and using exchange rates. Read more about this in <a href=\"https:\/\/docs.firefly-iii.org\/LOL_NOT_FINISHED_YET_TODO\">the documentation<\/a>.",
"exchange_rates_from_to": "Between {from} and {to} (and the other way around)",
"exchange_rates_intro_rates": "Firefly III uses the following exchange rates. The inverse is automatically calculated when it is not provided. If no exchange rate exists for the date of the transaction, Firefly III will go back in time to find one. If none are present, the rate \"1\" will be used.",
"header_exchange_rates_rates": "Exchange rates",
"header_exchange_rates_table": "Table with exchange rates",
"help_rate_form": "On this day, how many {to} will you get for one {from}?",
"add_new_rate": "Add a new exchange rate",
"save_new_rate": "Save new rate"
"header_exchange_rates": "Wisselkoersen",
"exchange_rates_intro": "Firefly III kan wisselkoersen downloaden en gebruiken. Lees hier meer over in <a href=\"https:\/\/docs.firefly-iii.org\/LOL_NOT_FINISHED_YET_TODO\">de documentatie<\/a>.",
"exchange_rates_from_to": "Tussen {from} en {to} (en andersom)",
"exchange_rates_intro_rates": "Firefly III gebruikt de volgende wisselkoersen. De inverse berekent zichzelf als deze niet is opgegeven. Als er geen wisselkoers bestaat voor de datum van de transactie, gaat Firefly III terug in de tijd om er een te vinden. Als er geen aanwezig is, zal de koers \"1\" gebruikt worden.",
"header_exchange_rates_rates": "Wisselkoersen",
"header_exchange_rates_table": "Tabel met wisselkoersen",
"help_rate_form": "Hoeveel {to} krijg je op deze dag voor \u00e9\u00e9n {from}?",
"add_new_rate": "Nieuwe wisselkoers toevoegen",
"save_new_rate": "Nieuwe wisselkoers opslaan"
},
"form": {
"url": "URL",
@@ -158,7 +158,7 @@
"webhook_delivery": "Bericht",
"from_currency_to_currency": "{from} &rarr; {to}",
"to_currency_from_currency": "{to} &rarr; {from}",
"rate": "Rate"
"rate": "Wisselkoers"
},
"list": {
"active": "Actief?",

View File

@@ -217,9 +217,9 @@ return [
'button_register' => 'Register',
'authorization' => 'Authorization',
'active_bills_only' => 'active subscription only',
'active_bills_only_total' => 'all active subscription',
'active_exp_bills_only' => 'active and expected subscription only',
'active_exp_bills_only_total' => 'all active expected subscription only',
'active_bills_only_total' => 'all active subscriptions',
'active_exp_bills_only' => 'active and expected subscriptions only',
'active_exp_bills_only_total' => 'all active expected subscriptions only',
'per_period_sum_1D' => 'Expected daily costs',
'per_period_sum_1W' => 'Expected weekly costs',
'per_period_sum_1M' => 'Expected monthly costs',
@@ -1328,7 +1328,7 @@ return [
'rule_for_bill_title' => 'Auto-generated rule for subscription ":name"',
'rule_for_bill_description' => 'This rule is auto-generated to try to match subscription ":name".',
'create_rule_for_bill' => 'Create a new rule for subscription ":name"',
'create_rule_for_bill_txt' => 'You have just created a new subscription called ":name", congratulations!Firefly III can automagically match new withdrawals to this subscription. For example, whenever you pay your rent, the subscription "rent" will be linked to the expense. This way, Firefly III can accurately show you which subscriptions are due and which ones aren\'t. In order to do so, a new rule must be created. Firefly III has filled in some sensible defaults for you. Please make sure these are correct. If these values are correct, Firefly III will automatically link the correct withdrawal to the correct subscription. Please check out the triggers to see if they are correct, and add some if they\'re wrong.',
'create_rule_for_bill_txt' => 'You have just created a new subscription called ":name", congratulations! Firefly III can automagically match new withdrawals to this subscription. For example, whenever you pay your rent, the subscription "rent" will be linked to the expense. This way, Firefly III can accurately show you which subscriptions are due and which ones aren\'t. In order to do so, a new rule must be created. Firefly III has filled in some sensible defaults for you. Please make sure these are correct. If these values are correct, Firefly III will automatically link the correct withdrawal to the correct subscription. Please check out the triggers to see if they are correct, and add some if they\'re wrong.',
'new_rule_for_bill_title' => 'Rule for subscription ":name"',
'new_rule_for_bill_description' => 'This rule marks transactions for subscription ":name".',

View File

@@ -23,56 +23,57 @@
declare(strict_types=1);
return [
'main_message' => 'Action ":action", present in rule ":rule", could not be applied to transaction #:group: :error',
'find_or_create_tag_failed' => 'Could not find or create tag ":tag"',
'tag_already_added' => 'Tag ":tag" is already linked to this transaction',
'inspect_transaction' => 'Inspect transaction ":title" @ Firefly III',
'inspect_rule' => 'Inspect rule ":title" @ Firefly III',
'journal_other_user' => 'This transaction doesn\'t belong to the user',
'no_such_journal' => 'This transaction doesn\'t exist',
'journal_already_no_budget' => 'This transaction has no budget, so it cannot be removed',
'journal_already_no_category' => 'This transaction had no category, so it cannot be removed',
'journal_already_no_notes' => 'This transaction had no notes, so they cannot be removed',
'journal_not_found' => 'Firefly III can\'t find the requested transaction',
'split_group' => 'Firefly III cannot execute this action on a transaction with multiple splits',
'is_already_withdrawal' => 'This transaction is already a withdrawal',
'is_already_deposit' => 'This transaction is already a deposit',
'is_already_transfer' => 'This transaction is already a transfer',
'is_not_transfer' => 'This transaction is not a transfer',
'complex_error' => 'Something complicated went wrong. Sorry about that. Please inspect the logs of Firefly III',
'no_valid_opposing' => 'Conversion failed because there is no valid account named ":account"',
'new_notes_empty' => 'The notes to be set are empty',
'unsupported_transaction_type_withdrawal' => 'Firefly III cannot convert a ":type" to a withdrawal',
'unsupported_transaction_type_deposit' => 'Firefly III cannot convert a ":type" to a deposit',
'unsupported_transaction_type_transfer' => 'Firefly III cannot convert a ":type" to a transfer',
'already_has_source_asset' => 'This transaction already has ":name" as the source asset account',
'already_has_destination_asset' => 'This transaction already has ":name" as the destination asset account',
'already_has_destination' => 'This transaction already has ":name" as the destination account',
'already_has_source' => 'This transaction already has ":name" as the source account',
'already_linked_to_subscription' => 'The transaction is already linked to subscription ":name"',
'already_linked_to_category' => 'The transaction is already linked to category ":name"',
'already_linked_to_budget' => 'The transaction is already linked to budget ":name"',
'cannot_find_subscription' => 'Firefly III can\'t find subscription ":name"',
'no_notes_to_move' => 'The transaction has no notes to move to the description field',
'no_tags_to_remove' => 'The transaction has no tags to remove',
'not_withdrawal' => 'The transaction is not a withdrawal',
'not_deposit' => 'The transaction is not a deposit',
'cannot_find_tag' => 'Firefly III can\'t find tag ":tag"',
'cannot_find_asset' => 'Firefly III can\'t find asset account ":name"',
'cannot_find_accounts' => 'Firefly III can\'t find the source or destination account',
'cannot_find_source_transaction' => 'Firefly III can\'t find the source transaction',
'cannot_find_destination_transaction' => 'Firefly III can\'t find the destination transaction',
'cannot_find_source_transaction_account' => 'Firefly III can\'t find the source transaction account',
'cannot_find_destination_transaction_account' => 'Firefly III can\'t find the destination transaction account',
'cannot_find_piggy' => 'Firefly III can\'t find a piggy bank named ":name"',
'no_link_piggy' => 'This transaction\'s accounts are not linked to the piggy bank, so no action will be taken',
'cannot_unlink_tag' => 'Tag ":tag" isn\'t linked to this transaction',
'cannot_find_budget' => 'Firefly III can\'t find budget ":name"',
'cannot_find_category' => 'Firefly III can\'t find category ":name"',
'cannot_set_budget' => 'Firefly III can\'t set budget ":name" to a transaction of type ":type"',
'journal_invalid_amount' => 'Firefly III can\'t set amount ":amount" because it is not a valid number.',
'cannot_remove_zero_piggy' => 'Cannot remove zero amount from piggy bank ":name"',
'cannot_remove_from_piggy' => 'Cannot remove ":amount" from piggy bank ":name"',
'cannot_add_zero_piggy' => 'Cannot add zero amount to piggy bank ":name"',
'cannot_add_to_piggy' => 'Cannot add ":amount" to piggy bank ":name"',
'main_message' => 'Action ":action", present in rule ":rule", could not be applied to transaction #:group: :error',
'find_or_create_tag_failed' => 'Could not find or create tag ":tag"',
'tag_already_added' => 'Tag ":tag" is already linked to this transaction',
'inspect_transaction' => 'Inspect transaction ":title" @ Firefly III',
'inspect_rule' => 'Inspect rule ":title" @ Firefly III',
'journal_other_user' => 'This transaction doesn\'t belong to the user',
'no_such_journal' => 'This transaction doesn\'t exist',
'journal_already_no_budget' => 'This transaction has no budget, so it cannot be removed',
'journal_already_no_category' => 'This transaction had no category, so it cannot be removed',
'journal_already_no_notes' => 'This transaction had no notes, so they cannot be removed',
'journal_not_found' => 'Firefly III can\'t find the requested transaction',
'split_group' => 'Firefly III cannot execute this action on a transaction with multiple splits',
'is_already_withdrawal' => 'This transaction is already a withdrawal',
'is_already_deposit' => 'This transaction is already a deposit',
'is_already_transfer' => 'This transaction is already a transfer',
'is_not_transfer' => 'This transaction is not a transfer',
'complex_error' => 'Something complicated went wrong. Sorry about that. Please inspect the logs of Firefly III',
'no_valid_opposing' => 'Conversion failed because there is no valid account named ":account"',
'new_notes_empty' => 'The notes to be set are empty',
'unsupported_transaction_type_withdrawal' => 'Firefly III cannot convert a ":type" to a withdrawal',
'unsupported_transaction_type_deposit' => 'Firefly III cannot convert a ":type" to a deposit',
'unsupported_transaction_type_transfer' => 'Firefly III cannot convert a ":type" to a transfer',
'already_has_source_asset' => 'This transaction already has ":name" as the source asset account',
'already_has_destination_asset' => 'This transaction already has ":name" as the destination asset account',
'already_has_destination' => 'This transaction already has ":name" as the destination account',
'already_has_source' => 'This transaction already has ":name" as the source account',
'already_linked_to_subscription' => 'The transaction is already linked to subscription ":name"',
'already_linked_to_category' => 'The transaction is already linked to category ":name"',
'already_linked_to_budget' => 'The transaction is already linked to budget ":name"',
'cannot_find_subscription' => 'Firefly III can\'t find subscription ":name"',
'no_notes_to_move' => 'The transaction has no notes to move to the description field',
'no_tags_to_remove' => 'The transaction has no tags to remove',
'not_withdrawal' => 'The transaction is not a withdrawal',
'not_deposit' => 'The transaction is not a deposit',
'cannot_find_tag' => 'Firefly III can\'t find tag ":tag"',
'cannot_find_asset' => 'Firefly III can\'t find asset account ":name"',
'cannot_find_accounts' => 'Firefly III can\'t find the source or destination account',
'cannot_find_source_transaction' => 'Firefly III can\'t find the source transaction',
'cannot_find_destination_transaction' => 'Firefly III can\'t find the destination transaction',
'cannot_find_source_transaction_account' => 'Firefly III can\'t find the source transaction account',
'cannot_find_destination_transaction_account' => 'Firefly III can\'t find the destination transaction account',
'cannot_find_piggy' => 'Firefly III can\'t find a piggy bank named ":name"',
'no_link_piggy' => 'This transaction\'s accounts are not linked to the piggy bank, so no action will be taken',
'both_link_piggy' => 'This transaction\'s accounts are both linked to the piggy bank, so no action will be taken',
'cannot_unlink_tag' => 'Tag ":tag" isn\'t linked to this transaction',
'cannot_find_budget' => 'Firefly III can\'t find budget ":name"',
'cannot_find_category' => 'Firefly III can\'t find category ":name"',
'cannot_set_budget' => 'Firefly III can\'t set budget ":name" to a transaction of type ":type"',
'journal_invalid_amount' => 'Firefly III can\'t set amount ":amount" because it is not a valid number.',
'cannot_remove_zero_piggy' => 'Cannot remove zero amount from piggy bank ":name"',
'cannot_remove_from_piggy' => 'Cannot remove ":amount" from piggy bank ":name"',
'cannot_add_zero_piggy' => 'Cannot add zero amount to piggy bank ":name"',
'cannot_add_to_piggy' => 'Cannot add ":amount" to piggy bank ":name"',
];

View File

@@ -23,11 +23,10 @@
</td>
<td style="text-align: right;">
{% if event.amount < 0 %}
<span class="text-danger money-negative">{{ trans('firefly.removed_amount', {amount: formatAmountBySymbol(event.amount,event.piggyBank.transactionCurrency.symbol, false)})|raw }}</span>
<span class="text-danger money-negative">{{ trans('firefly.removed_amount', {amount: formatAmountBySymbol(event.amount,event.piggyBank.transactionCurrency.symbol, event.piggyBank.transactionCurrency.decimal_places, false)})|raw }}</span>
{% else %}
<span class="text-success money-positive">{{ trans('firefly.added_amount', {amount: formatAmountBySymbol(event.amount, event.piggyBank.transactionCurrency.symbol, false)})|raw }}</span>
<span class="text-success money-positive">{{ trans('firefly.added_amount', {amount: formatAmountBySymbol(event.amount, event.piggyBank.transactionCurrency.symbol, event.piggyBank.transactionCurrency.decimal_places, false)})|raw }}</span>
{% endif %}
</td>
</tr>