Compare commits

...

11 Commits

Author SHA1 Message Date
github-actions[bot]
dba8bba41a Merge pull request #10177 from firefly-iii/release-1745301598
🤖 Automatically merge the PR into the develop branch.
2025-04-22 08:00:06 +02:00
JC5
55a00aa6fe 🤖 Auto commit for release 'develop' on 2025-04-22 2025-04-22 07:59:58 +02:00
James Cole
22133f64cf Merge pull request #10176 from firefly-iii/fix-null
Fix nullpointer
2025-04-22 07:56:01 +02:00
Sander Dorigo
2efb2377b6 Fix nullpointer 2025-04-22 07:55:14 +02:00
James Cole
9f75a96ad6 Merge pull request #10172 from firefly-iii/dependabot/npm_and_yarn/develop/i18next-25.0.1
Bump i18next from 24.2.3 to 25.0.1
2025-04-21 08:04:29 +02:00
James Cole
d0be9bb957 Merge pull request #10174 from firefly-iii/fix-10114
Fix #10114
2025-04-21 08:04:16 +02:00
James Cole
c25adf0a56 Fix #10114 2025-04-21 08:03:32 +02:00
mergify[bot]
0ca79fd843 Merge branch 'develop' into dependabot/npm_and_yarn/develop/i18next-25.0.1 2025-04-21 05:17:23 +00:00
James Cole
efc516eb3b Merge pull request #10173 from firefly-iii/main
Merge pull request #10170 from firefly-iii/develop
2025-04-21 07:16:48 +02:00
dependabot[bot]
dca3ac9250 Bump i18next from 24.2.3 to 25.0.1
Bumps [i18next](https://github.com/i18next/i18next) from 24.2.3 to 25.0.1.
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next/compare/v24.2.3...v25.0.1)

---
updated-dependencies:
- dependency-name: i18next
  dependency-version: 25.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-21 04:07:28 +00:00
github-actions[bot]
85aee63d1e Merge pull request #10170 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2025-04-20 21:22:09 +02:00
12 changed files with 83 additions and 47 deletions

View File

@@ -45,7 +45,13 @@ class PiggyBankEventObserver
private function updateNativeAmount(PiggyBankEvent $event): void
{
if (!Amount::convertToNative($event->piggyBank->accounts()->first()->user)) {
$user = $event->piggyBank->accounts()->first()?->user;
if (null === $user) {
Log::warning('Piggy bank seems to have no accounts. Break.');
return;
}
if (!Amount::convertToNative($user)) {
return;
}
$userCurrency = app('amount')->getNativeCurrencyByUserGroup($event->piggyBank->accounts()->first()->user->userGroup);

View File

@@ -177,7 +177,7 @@ class IndexController extends Controller
$array['end_date'] = $entry->end_date;
// spent in period:
$spentArr = $this->opsRepository->sumExpenses($entry->start_date, $entry->end_date, null, null, $entry->transactionCurrency);
$spentArr = $this->opsRepository->sumExpenses($entry->start_date, $entry->end_date, null, null, $entry->transactionCurrency, false);
$array['spent'] = $spentArr[$entry->transaction_currency_id]['sum'] ?? '0';
$array['native_spent'] = $this->convertToNative && $entry->transaction_currency_id !== $this->defaultCurrency->id ? $converter->convert($entry->transactionCurrency, $this->defaultCurrency, $entry->start_date, $array['spent']) : null;
// budgeted in period:
@@ -235,7 +235,7 @@ class IndexController extends Controller
/** @var TransactionCurrency $currency */
foreach ($currencies as $currency) {
$spentArr = $this->opsRepository->sumExpenses($start, $end, null, new Collection([$current]), $currency);
$spentArr = $this->opsRepository->sumExpenses($start, $end, null, new Collection([$current]), $currency, false);
if (array_key_exists($currency->id, $spentArr) && array_key_exists('sum', $spentArr[$currency->id])) {
$array['spent'][$currency->id]['spent'] = $spentArr[$currency->id]['sum'];
$array['spent'][$currency->id]['currency_id'] = $currency->id;

View File

@@ -175,9 +175,10 @@ class ShowController extends Controller
throw new FireflyException('This budget limit is not part of this budget.');
}
$page = (int) $request->get('page');
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
$subTitle = trans(
$currencySymbol = $budgetLimit->transactionCurrency->symbol;
$page = (int) $request->get('page');
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
$subTitle = trans(
'firefly.budget_in_period',
[
'name' => $budget->name,
@@ -186,23 +187,26 @@ class ShowController extends Controller
'currency' => $budgetLimit->transactionCurrency->name,
]
);
if ($this->convertToNative) {
$currencySymbol = $this->defaultCurrency->symbol;
}
// collector:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector = app(GroupCollectorInterface::class);
$collector->setRange($budgetLimit->start_date, $budgetLimit->end_date)->withAccountInformation()
->setBudget($budget)->setLimit($pageSize)->setPage($page)->withBudgetInformation()->withCategoryInformation()
;
$groups = $collector->getPaginatedGroups();
$groups = $collector->getPaginatedGroups();
$groups->setPath(route('budgets.show.limit', [$budget->id, $budgetLimit->id]));
/** @var Carbon $start */
$start = session('first', today(config('app.timezone'))->startOfYear());
$end = today(config('app.timezone'));
$attachments = $this->repository->getAttachments($budget);
$limits = $this->getLimits($budget, $start, $end);
$start = session('first', today(config('app.timezone'))->startOfYear());
$end = today(config('app.timezone'));
$attachments = $this->repository->getAttachments($budget);
$limits = $this->getLimits($budget, $start, $end);
return view('budgets.show', compact('limits', 'attachments', 'budget', 'budgetLimit', 'groups', 'subTitle'));
return view('budgets.show', compact('limits', 'attachments', 'budget', 'budgetLimit', 'groups', 'subTitle', 'currencySymbol'));
}
}

View File

@@ -172,13 +172,13 @@ class BudgetController extends Controller
$budgetCollection = new Collection([$budget]);
$currency = $budgetLimit->transactionCurrency;
if ($this->convertToNative) {
$amount = $budgetLimit->native_amount ?? '0';
$amount = $budgetLimit->native_amount ?? $amount;
$currency = $this->defaultCurrency;
}
while ($start <= $end) {
$current = clone $start;
$expenses = $this->opsRepository->sumExpenses($current, $current, null, $budgetCollection, $budgetLimit->transactionCurrency);
$expenses = $this->opsRepository->sumExpenses($current, $current, null, $budgetCollection, $budgetLimit->transactionCurrency, $this->convertToNative);
$spent = $expenses[$currency->id]['sum'] ?? '0';
$amount = bcadd($amount, $spent);
$format = $start->isoFormat((string) trans('config.month_and_day_js', [], $locale));
@@ -191,6 +191,7 @@ class BudgetController extends Controller
$data['datasets'][0]['currency_symbol'] = $currency->symbol;
$data['datasets'][0]['currency_code'] = $currency->code;
$cache->store($data);
// var_dump($data);exit;
return response()->json($data);
}
@@ -214,7 +215,7 @@ class BudgetController extends Controller
if (null !== $budgetLimit) {
$start = $budgetLimit->start_date;
$end = $budgetLimit->end_date;
$collector->setRange($budgetLimit->start_date, $budgetLimit->end_date)->setCurrency($budgetLimit->transactionCurrency);
$collector->setRange($budgetLimit->start_date, $budgetLimit->end_date)->setNormalCurrency($budgetLimit->transactionCurrency);
}
$cache->addProperty($start);
$cache->addProperty($end);
@@ -296,7 +297,7 @@ class BudgetController extends Controller
if (null !== $budgetLimit) {
$start = $budgetLimit->start_date;
$end = $budgetLimit->end_date;
$collector->setCurrency($budgetLimit->transactionCurrency);
$collector->setNormalCurrency($budgetLimit->transactionCurrency);
}
$cache->addProperty($start);
$cache->addProperty($end);
@@ -378,7 +379,7 @@ class BudgetController extends Controller
if (null !== $budgetLimit) {
$start = $budgetLimit->start_date;
$end = $budgetLimit->end_date;
$collector->setRange($budgetLimit->start_date, $budgetLimit->end_date)->setCurrency($budgetLimit->transactionCurrency);
$collector->setRange($budgetLimit->start_date, $budgetLimit->end_date)->setNormalCurrency($budgetLimit->transactionCurrency);
}
$cache->addProperty($start);
$cache->addProperty($end);

View File

@@ -31,6 +31,7 @@ use FireflyIII\Models\Account;
use FireflyIII\Models\Budget;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Report\Summarizer\TransactionSummarizer;
use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
@@ -201,9 +202,10 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
Carbon $end,
?Collection $accounts = null,
?Collection $budgets = null,
?TransactionCurrency $currency = null
?TransactionCurrency $currency = null,
bool $convertToNative = false
): array {
Log::debug(sprintf('Start of %s.', __METHOD__));
Log::debug(sprintf('Start of %s(date, date, array, array, "%s", "%s").', __METHOD__, $currency?->code, var_export($convertToNative, true)));
// this collector excludes all transfers TO liabilities (which are also withdrawals)
// because those expenses only become expenses once they move from the liability to the friend.
// 2024-12-24 disable the exclusion for now.
@@ -235,8 +237,8 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
$budgets = $this->getBudgets();
}
if (null !== $currency) {
Log::debug(sprintf('Limit to currency %s', $currency->code));
$collector->setCurrency($currency);
Log::debug(sprintf('Limit to normal currency %s', $currency->code));
$collector->setNormalCurrency($currency);
}
$collector->setBudgets($budgets);
$journals = $collector->getExtractedJournals();
@@ -246,6 +248,8 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
Log::debug('STOP looking for transactions in the foreign currency.');
}
$summarizer = new TransactionSummarizer($this->user);
// 2025-04-21 overrule "convertToNative" because in this particular view, we never want to do this.
$summarizer->setConvertToNative($convertToNative);
return $summarizer->groupByCurrencyId($journals, 'negative', false);
}

View File

@@ -38,6 +38,7 @@ use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
/**
* Trait AugumentData
@@ -167,6 +168,8 @@ trait AugumentData
*/
protected function getLimits(Budget $budget, Carbon $start, Carbon $end): Collection // get data + augment with info
{
Log::debug('In getLimits');
/** @var OperationsRepositoryInterface $opsRepository */
$opsRepository = app(OperationsRepositoryInterface::class);
@@ -195,24 +198,30 @@ trait AugumentData
/** @var BudgetLimit $entry */
foreach ($set as $entry) {
$currency = $entry->transactionCurrency;
Log::debug(sprintf('Now at budget limit #%d', $entry->id));
$currency = $entry->transactionCurrency;
if ($this->convertToNative) {
// the sumExpenses method already handles this.
$currency = $this->defaultCurrency;
}
// clone because these objects change each other.
$currentStart = clone $entry->start_date;
$currentEnd = null === $entry->end_date ? null : clone $entry->end_date;
$currentStart = clone $entry->start_date;
$currentEnd = null === $entry->end_date ? null : clone $entry->end_date;
if (null === $currentEnd) {
$currentEnd = clone $currentStart;
$currentEnd->addMonth();
}
// native amount.
$expenses = $opsRepository->sumExpenses($currentStart, $currentEnd, null, $budgetCollection, $entry->transactionCurrency, $this->convertToNative);
$spent = $expenses[$currency->id]['sum'] ?? '0';
$entry->native_spent = $spent;
$expenses = $opsRepository->sumExpenses($currentStart, $currentEnd, null, $budgetCollection, $currency);
$spent = $expenses[$currency->id]['sum'] ?? '0';
$entry->spent = $spent;
// normal amount:
$expenses = $opsRepository->sumExpenses($currentStart, $currentEnd, null, $budgetCollection, $entry->transactionCurrency, false);
$spent = $expenses[$entry->transactionCurrency->id]['sum'] ?? '0';
$entry->spent = $spent;
$limits->push($entry);
}

View File

@@ -51,7 +51,7 @@ class TransactionSummarizer
public function groupByCurrencyId(array $journals, string $method = 'negative', bool $includeForeign = true): array
{
Log::debug(sprintf('Now in groupByCurrencyId(array, "%s")', $method));
Log::debug(sprintf('Now in groupByCurrencyId([%d journals], "%s")', count($journals), $method));
$array = [];
foreach ($journals as $journal) {
$field = 'amount';
@@ -71,6 +71,7 @@ class TransactionSummarizer
$foreignCurrencyDecimalPlaces = null;
if ($this->convertToNative) {
Log::debug('convertToNative is true.');
// if convert to native, use the native amount yes or no?
$useNative = $this->default->id !== (int) $journal['currency_id'];
$useForeign = $this->default->id === (int) $journal['foreign_currency_id'];
@@ -94,6 +95,7 @@ class TransactionSummarizer
}
}
if (!$this->convertToNative) {
Log::debug('convertToNative is false.');
// use foreign amount?
$foreignCurrencyId = (int) $journal['foreign_currency_id'];
if (0 !== $foreignCurrencyId) {
@@ -224,4 +226,10 @@ class TransactionSummarizer
return $array;
}
public function setConvertToNative(bool $convertToNative): void
{
Log::debug(sprintf('Overrule convertToNative to become %s', var_export($convertToNative, true)));
$this->convertToNative = $convertToNative;
}
}

View File

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

28
package-lock.json generated
View File

@@ -5635,9 +5635,9 @@
"license": "MIT"
},
"node_modules/electron-to-chromium": {
"version": "1.5.139",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.139.tgz",
"integrity": "sha512-GGnRYOTdN5LYpwbIr0rwP/ZHOQSvAF6TG0LSzp28uCBb9JiXHJGmaaKw29qjNJc5bGnnp6kXJqRnGMQoELwi5w==",
"version": "1.5.140",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.140.tgz",
"integrity": "sha512-o82Rj+ONp4Ip7Cl1r7lrqx/pXhbp/lh9DpKcMNscFJdh8ebyRofnc7Sh01B4jx403RI0oqTBvlZ7OBIZLMr2+Q==",
"dev": true,
"license": "ISC"
},
@@ -6979,9 +6979,9 @@
}
},
"node_modules/i18next": {
"version": "24.2.3",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-24.2.3.tgz",
"integrity": "sha512-lfbf80OzkocvX7nmZtu7nSTNbrTYR52sLWxPtlXX1zAhVw8WEnFk4puUkCR4B1dNQwbSpEHHHemcZu//7EcB7A==",
"version": "25.0.1",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.0.1.tgz",
"integrity": "sha512-8S8PyZbrymJZn3DaN70/34JYWNhsqrU6yA4MuzcygJBv+41dgNMocEA8h+kV1P7MCc1ll03lOTOIXE7mpNCicw==",
"funding": [
{
"type": "individual",
@@ -7489,14 +7489,14 @@
"license": "MIT"
},
"node_modules/json-stable-stringify": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.2.1.tgz",
"integrity": "sha512-Lp6HbbBgosLmJbjx0pBLbgvx68FaFU1sdkmBuckmhhJ88kL13OA51CDtR2yJB50eCNMH9wRqtQNNiAqQH4YXnA==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.3.0.tgz",
"integrity": "sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.8",
"call-bound": "^1.0.3",
"call-bound": "^1.0.4",
"isarray": "^2.0.5",
"jsonify": "^0.0.1",
"object-keys": "^1.1.1"
@@ -10083,9 +10083,9 @@
"license": "MIT"
},
"node_modules/sass": {
"version": "1.86.3",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.86.3.tgz",
"integrity": "sha512-iGtg8kus4GrsGLRDLRBRHY9dNVA78ZaS7xr01cWnS7PEMQyFtTqBiyCrfpTYTZXRWM94akzckYjh8oADfFNTzw==",
"version": "1.87.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.87.0.tgz",
"integrity": "sha512-d0NoFH4v6SjEK7BoX810Jsrhj7IQSYHAHLi/iSpgqKc7LaIDshFRlSg5LOymf9FqQhxEHs2W5ZQXlvy0KD45Uw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -12337,7 +12337,7 @@
"chartjs-adapter-date-fns": "^3.0.0",
"chartjs-chart-sankey": "^0.14.0",
"date-fns": "^4.0.0",
"i18next": "^24.2.0",
"i18next": "^25.0.1",
"i18next-chained-backend": "^4.6.2",
"i18next-http-backend": "^3.0.1",
"i18next-localstorage-backend": "^4.2.0",

View File

@@ -27,7 +27,7 @@
"chartjs-adapter-date-fns": "^3.0.0",
"chartjs-chart-sankey": "^0.14.0",
"date-fns": "^4.0.0",
"i18next": "^24.2.0",
"i18next": "^25.0.1",
"i18next-chained-backend": "^4.6.2",
"i18next-http-backend": "^3.0.1",
"i18next-localstorage-backend": "^4.2.0",

View File

@@ -314,6 +314,7 @@
{% endif %}
</td>
<td class="hidden-sm hidden-xs spent" data-id="{{ budget.id }}" style="text-align:right;">
{% for spentInfo in budget.spent %}
{{ formatAmountBySymbol(spentInfo.spent, spentInfo.currency_symbol, spentInfo.currency_decimal_places) }}
{% if 0 == activeDaysPassed %}

View File

@@ -167,7 +167,10 @@
<td style="width:33%;">{{ 'spent'|_ }}</td>
<td>
{% if convertToNative %}
{{ formatAmountBySymbol(limit.spent, defaultCurrency.symbol, defaultCurrency.decimal_places) }}
{{ formatAmountBySymbol(limit.spent, limit.transactionCurrency.symbol, limit.transactionCurrency.decimal_places) }}
{% if limit.native_spent %}
({{ formatAmountBySymbol(limit.native_spent, defaultCurrency.symbol, defaultCurrency.decimal_places) }})
{% endif %}
{% else %}
{{ formatAmountBySymbol(limit.spent, limit.transactionCurrency.symbol, limit.transactionCurrency.decimal_places) }}
{% endif %}
@@ -215,7 +218,7 @@
{% if budgetLimit.id %}
budgetLimitID = {{ budgetLimit.id }};
var budgetChartUrl = '{{ route('chart.budget.budget-limit', [budget.id, budgetLimit.id] ) }}';
var currencySymbol = '{{ budgetLimit.transactionCurrency.symbol }}';
var currencySymbol = '{{ currencySymbol }}';
var expenseCategoryUrl = '{{ route('chart.budget.expense-category', [budget.id, budgetLimit.id]) }}';
var expenseAssetUrl = '{{ route('chart.budget.expense-asset', [budget.id, budgetLimit.id]) }}';
var expenseExpenseUrl = '{{ route('chart.budget.expense-expense', [budget.id, budgetLimit.id]) }}';