diff --git a/app/Api/V1/Controllers/Insight/Income/TagController.php b/app/Api/V1/Controllers/Insight/Income/TagController.php index 94be2ae461..df99eef909 100644 --- a/app/Api/V1/Controllers/Insight/Income/TagController.php +++ b/app/Api/V1/Controllers/Insight/Income/TagController.php @@ -158,10 +158,7 @@ final class TagController extends Controller 'currency_id' => (string) $foreignCurrencyId, 'currency_code' => $journal['foreign_currency_code'], ]; - $response[$foreignKey]['difference'] = bcadd( - (string) $response[$foreignKey]['difference'], - Steam::positive($journal['foreign_amount']) - ); + $response[$foreignKey]['difference'] = bcadd((string) $response[$foreignKey]['difference'], Steam::positive($journal['foreign_amount'])); $response[$foreignKey]['difference_float'] = (float) $response[$foreignKey]['difference']; } } diff --git a/app/Api/V1/Controllers/Insight/Transfer/TagController.php b/app/Api/V1/Controllers/Insight/Transfer/TagController.php index b7b85fd376..f92a7aa9f0 100644 --- a/app/Api/V1/Controllers/Insight/Transfer/TagController.php +++ b/app/Api/V1/Controllers/Insight/Transfer/TagController.php @@ -155,10 +155,7 @@ final class TagController extends Controller 'currency_id' => (string) $foreignCurrencyId, 'currency_code' => $journal['foreign_currency_code'], ]; - $response[$foreignKey]['difference'] = bcadd( - (string) $response[$foreignKey]['difference'], - Steam::positive($journal['foreign_amount']) - ); + $response[$foreignKey]['difference'] = bcadd((string) $response[$foreignKey]['difference'], Steam::positive($journal['foreign_amount'])); $response[$foreignKey]['difference_float'] = (float) $response[$foreignKey]['difference']; // intentional float } } diff --git a/app/Http/Controllers/Account/ShowController.php b/app/Http/Controllers/Account/ShowController.php index cef8f2b9f2..1c049762e2 100644 --- a/app/Http/Controllers/Account/ShowController.php +++ b/app/Http/Controllers/Account/ShowController.php @@ -224,9 +224,7 @@ final class ShowController extends Controller /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); - $collector->setAccounts(new Collection()->push($account)) - - ->withAPIInformation()->setLimit($pageSize)->setPage($page)->withAccountInformation(); + $collector->setAccounts(new Collection()->push($account))->withAPIInformation()->setLimit($pageSize)->setPage($page)->withAccountInformation(); // this search will not include transaction groups where this asset account (or liability) // is just part of ONE of the journals. To force this: diff --git a/app/Http/Controllers/Budget/BudgetLimitController.php b/app/Http/Controllers/Budget/BudgetLimitController.php index d461374baa..2edb4f5531 100644 --- a/app/Http/Controllers/Budget/BudgetLimitController.php +++ b/app/Http/Controllers/Budget/BudgetLimitController.php @@ -198,13 +198,7 @@ final class BudgetLimitController extends Controller if ($request->expectsJson()) { $array = $limit->toArray(); // add some extra metadata: - $spentArr = $this->opsRepository->sumExpenses( - $limit->start_date, - $limit->end_date, - null, - new Collection()->push($budget), - $currency - ); + $spentArr = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection()->push($budget), $currency); $array['spent'] = $spentArr[$currency->id]['sum'] ?? '0'; $array['left_formatted'] = Amount::formatAnything($limit->transactionCurrency, bcadd($array['spent'], (string) $array['amount'])); $array['amount_formatted'] = Amount::formatAnything($limit->transactionCurrency, $limit['amount']); diff --git a/app/Http/Controllers/Budget/IndexController.php b/app/Http/Controllers/Budget/IndexController.php index 52c6f900e3..65429e59c1 100644 --- a/app/Http/Controllers/Budget/IndexController.php +++ b/app/Http/Controllers/Budget/IndexController.php @@ -272,10 +272,7 @@ final class IndexController extends Controller 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]['spent_outside'] = Steam::negative(bcsub( - $spentInLimits[$currency->id], - $spentArr[$currency->id]['sum'] - )); + $array['spent'][$currency->id]['spent_outside'] = Steam::negative(bcsub($spentInLimits[$currency->id], $spentArr[$currency->id]['sum'])); $array['spent'][$currency->id]['currency_id'] = $currency->id; $array['spent'][$currency->id]['currency_symbol'] = $currency->symbol; $array['spent'][$currency->id]['currency_decimal_places'] = $currency->decimal_places; diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index f4b32fe5c5..ad724137dc 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -539,13 +539,7 @@ final class BudgetController extends Controller } // get spent amount in this period for this currency. - $sum = $this->opsRepository->sumExpenses( - $currentStart, - $currentEnd, - $accounts, - new Collection()->push($budget), - $currency - ); + $sum = $this->opsRepository->sumExpenses($currentStart, $currentEnd, $accounts, new Collection()->push($budget), $currency); $amount = Steam::positive($sum[$currency->id]['sum'] ?? '0'); $chartData[0]['entries'][$title] = Steam::bcround($amount, $currency->decimal_places); diff --git a/app/Http/Controllers/Json/ReconcileController.php b/app/Http/Controllers/Json/ReconcileController.php index d05c5e9de9..38f2e4b9bb 100644 --- a/app/Http/Controllers/Json/ReconcileController.php +++ b/app/Http/Controllers/Json/ReconcileController.php @@ -185,8 +185,8 @@ final class ReconcileController extends Controller $startDate->subDay(); // validate from or exit - $from = $request->input('_from'); - $validator = validator(['_from' => $from], ['nullable', 'max:255', new IsValidOriginUrl()]); + $from = $request->input('_from'); + $validator = validator(['_from' => $from], ['nullable', 'max:255', new IsValidOriginUrl()]); if ($validator->fails()) { throw new FireflyException(trans('validation.bad_url_parts')); } diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index 0948122483..a9ee91538b 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -32,7 +32,6 @@ use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\View as ViewFacade; -use Illuminate\View\View; use Throwable; use function Safe\mb_convert_encoding; @@ -125,7 +124,7 @@ final class SearchController extends Controller // validate from or exit $from = $request->input('_from'); - $validator = validator(['_from' => $from], ['nullable', 'max:255', new IsValidOriginUrl()]); + $validator = validator(['_from' => $from], ['nullable', 'max:255', new IsValidOriginUrl()]); if ($validator->fails()) { throw new FireflyException(trans('validation.bad_url_parts')); } diff --git a/app/Jobs/CreateAutoBudgetLimits.php b/app/Jobs/CreateAutoBudgetLimits.php index ca2d5f0255..cb2ef13f38 100644 --- a/app/Jobs/CreateAutoBudgetLimits.php +++ b/app/Jobs/CreateAutoBudgetLimits.php @@ -133,13 +133,7 @@ class CreateAutoBudgetLimits implements ShouldQueue // if has one, calculate expenses and use that as a base. $repository = app(OperationsRepositoryInterface::class); $repository->setUser($autoBudget->budget->user); - $spent = $repository->sumExpenses( - $previousStart, - $previousEnd, - null, - new Collection()->push($autoBudget->budget), - $autoBudget->transactionCurrency - ); + $spent = $repository->sumExpenses($previousStart, $previousEnd, null, new Collection()->push($autoBudget->budget), $autoBudget->transactionCurrency); $currencyId = $autoBudget->transaction_currency_id; $spentAmount = $spent[$currencyId]['sum'] ?? '0'; Log::debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount)); @@ -229,13 +223,7 @@ class CreateAutoBudgetLimits implements ShouldQueue // if has one, calculate expenses and use that as a base. $repository = app(OperationsRepositoryInterface::class); $repository->setUser($autoBudget->budget->user); - $spent = $repository->sumExpenses( - $previousStart, - $previousEnd, - null, - new Collection()->push($autoBudget->budget), - $autoBudget->transactionCurrency - ); + $spent = $repository->sumExpenses($previousStart, $previousEnd, null, new Collection()->push($autoBudget->budget), $autoBudget->transactionCurrency); $currencyId = $autoBudget->transaction_currency_id; $spentAmount = $spent[$currencyId]['sum'] ?? '0'; Log::debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount)); diff --git a/app/Support/Http/Controllers/AugumentData.php b/app/Support/Http/Controllers/AugumentData.php index a507156873..a3a940b1ba 100644 --- a/app/Support/Http/Controllers/AugumentData.php +++ b/app/Support/Http/Controllers/AugumentData.php @@ -222,14 +222,7 @@ trait AugumentData $currentEnd->addMonth(); } // primary currency amount. - $expenses = $opsRepository->sumExpenses( - $currentStart, - $currentEnd, - null, - $budgetCollection, - $entry->transactionCurrency, - $this->convertToPrimary - ); + $expenses = $opsRepository->sumExpenses($currentStart, $currentEnd, null, $budgetCollection, $entry->transactionCurrency, $this->convertToPrimary); $spent = $expenses[$currency->id]['sum'] ?? '0'; $entry->pc_spent = $spent; diff --git a/app/Support/JsonApi/Enrichments/RecurringEnrichment.php b/app/Support/JsonApi/Enrichments/RecurringEnrichment.php index 2428353cb0..d0c09bf027 100644 --- a/app/Support/JsonApi/Enrichments/RecurringEnrichment.php +++ b/app/Support/JsonApi/Enrichments/RecurringEnrichment.php @@ -354,11 +354,7 @@ class RecurringEnrichment implements EnrichmentInterface /** @var RecurrenceRepetition $repetition */ foreach ($set as $repetition) { - $recurrence = $this->collection->filter( - static fn (Recurrence $item): bool => (int) $item->id === (int) $repetition->recurrence_id - ) - ->first() - ; + $recurrence = $this->collection->filter(static fn (Recurrence $item): bool => (int) $item->id === (int) $repetition->recurrence_id)->first(); $fromDate = clone ($recurrence->latest_date ?? $recurrence->first_date); $recurrenceId = (int) $repetition->recurrence_id; $repId = (int) $repetition->id; diff --git a/changelog.md b/changelog.md index efb98c10e4..9d9a97ed84 100644 --- a/changelog.md +++ b/changelog.md @@ -11,11 +11,11 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Self-hosted users should no longer run into `Target class [has] does not exist` errors. - Explain in the release notes how to clear your cache, as suggested by @Kage1 -- #12972 -- #13022 -- #13034 -- #13037 -- #13039 +- [Issue 12972](https://github.com/firefly-iii/firefly-iii/issues/12972) (Updating transaction from account reconciliation results in JSON/HTML output) reported by @Tommy78649 +- [Issue 13022](https://github.com/firefly-iii/firefly-iii/issues/13022) (Category list does not render actions-menu) reported by @JC5 +- [Issue 13034](https://github.com/firefly-iii/firefly-iii/issues/13034) (Account list shows "January 1st" when there is no last activity) reported by @JC5 +- [Issue 13037](https://github.com/firefly-iii/firefly-iii/issues/13037) (Account lists are missing sums) reported by @JC5 +- [Issue 13039](https://github.com/firefly-iii/firefly-iii/issues/13039) (Budget column empty values on transaction view) reported by @no-coders ## v6.7.8 - 2026-10-10 diff --git a/composer.lock b/composer.lock index 68cd2ceea5..27ea377d0c 100644 --- a/composer.lock +++ b/composer.lock @@ -10215,16 +10215,16 @@ }, { "name": "carthage-software/mago", - "version": "1.55.0", + "version": "1.56.1", "source": { "type": "git", "url": "https://github.com/carthage-software/mago.git", - "reference": "2487de2818449cab3b7fde26be7b00d3a63f6a61" + "reference": "d5a5992e265e821fc9e6863971bed3dbe5d6cc98" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/carthage-software/mago/zipball/2487de2818449cab3b7fde26be7b00d3a63f6a61", - "reference": "2487de2818449cab3b7fde26be7b00d3a63f6a61", + "url": "https://api.github.com/repos/carthage-software/mago/zipball/d5a5992e265e821fc9e6863971bed3dbe5d6cc98", + "reference": "d5a5992e265e821fc9e6863971bed3dbe5d6cc98", "shasum": "" }, "require": { @@ -10266,7 +10266,7 @@ ], "support": { "issues": "https://github.com/carthage-software/mago/issues", - "source": "https://github.com/carthage-software/mago/tree/1.55.0" + "source": "https://github.com/carthage-software/mago/tree/1.56.1" }, "funding": [ { @@ -10274,7 +10274,7 @@ "type": "github" } ], - "time": "2026-10-09T22:43:14+00:00" + "time": "2026-10-11T13:37:01+00:00" }, { "name": "cloudcreativity/json-api-testing", diff --git a/config/firefly.php b/config/firefly.php index 467cf90d38..8da426e665 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -78,8 +78,8 @@ return [ 'running_balance_column' => (bool)env_default_when_empty(env('USE_RUNNING_BALANCE'), true), // this is only the default value, is not used. // see cer.php for exchange rates feature flag. ], -'version' => 'develop/2026-10-10', -'build_time' => 1791637300, +'version' => 'develop/2026-10-11', +'build_time' => 1791745670, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 28, // field is no longer used. diff --git a/resources/assets/v3/js/pages/accounts/index.js b/resources/assets/v3/js/pages/accounts/index.js index 95198be747..d72495ec00 100644 --- a/resources/assets/v3/js/pages/accounts/index.js +++ b/resources/assets/v3/js/pages/accounts/index.js @@ -22,14 +22,21 @@ import "../../boot/bootstrap.js"; import sidebar from "../shared/sidebar.js"; import dates from "../shared/dates.js"; import Alpine from "@alpinejs/csp"; -import {getVariable} from "../../store/get-variable.js"; +import { getVariable } from "../../store/get-variable.js"; import Put from "../../api/model/account/put.js"; import Get from "../../api/model/account/get.js"; import formatDate from "../../util/format-date.js"; import formatMoney from "../../util/format-money.js"; import i18next from "i18next"; -import {addDrag} from "../shared/drag-and-droppable-rows.js"; -import {initFromLocalStorage, initFromQueryParams, sortableTable, storeFilterAndSort, updateHistory, respondToSortChange} from "../shared/sortable-tables.js"; +import { addDrag } from "../shared/drag-and-droppable-rows.js"; +import { + initFromLocalStorage, + initFromQueryParams, + sortableTable, + storeFilterAndSort, + updateHistory, + respondToSortChange, +} from "../shared/sortable-tables.js"; window.enableDates = true; @@ -84,7 +91,8 @@ let index = function () { const address = window.location.href.split("?")[0].split("/"); this.objectType = address[address.length - 1].substring(0, 15); this.storageKey = "accounts-" + this.objectType + (this.active ? "-active" : "-inactive"); - this.defaultSortColumn = "expense" === this.objectType || "revenue" === this.objectType ? "name" : this.defaultSortColumn; + this.defaultSortColumn = + "expense" === this.objectType || "revenue" === this.objectType ? "name" : this.defaultSortColumn; this.i18next = i18next; this.anonymous = window.store.get("anonymous"); this.pageNavUrl = "./accounts/" + this.objectType; @@ -113,7 +121,7 @@ let index = function () { let item = e.detail[i]; if (item.order !== item.currentOrder) { // PUT new order to system. - new Put().put({order: item.order}, {id: item.id}); + new Put().put({ order: item.order }, { id: item.id }); // save new order as current order in the row. document @@ -158,7 +166,10 @@ let index = function () { }, downloadObjects() { this.sortableTable.loadingNewSort = true; - let sort = "asc" === this.sortableTable.sortDirection ? this.sortableTable.sortColumn : "-" + this.sortableTable.sortColumn; + let sort = + "asc" === this.sortableTable.sortDirection + ? this.sortableTable.sortColumn + : "-" + this.sortableTable.sortColumn; let start = window.store.get("start"); let end = window.store.get("end"); this.convertToPrimary = window.store.get("convert_to_primary"); @@ -205,7 +216,7 @@ let index = function () { } let lastActivity = formatDate( current.attributes.last_activity, - i18next.t("config.date_time_fns_short", {lng: locale}), + i18next.t("config.date_time_fns_short", { lng: locale }), locale, ); diff --git a/resources/assets/v3/js/pages/categories/index.js b/resources/assets/v3/js/pages/categories/index.js index e5f47465c5..a95eeacb24 100644 --- a/resources/assets/v3/js/pages/categories/index.js +++ b/resources/assets/v3/js/pages/categories/index.js @@ -25,7 +25,14 @@ import Alpine from "@alpinejs/csp"; import formatMoney from "../../util/format-money.js"; import formatDate from "../../util/format-date.js"; import i18next from "i18next"; -import {initFromLocalStorage, initFromQueryParams, sortableTable, storeFilterAndSort, updateHistory, respondToSortChange} from "../shared/sortable-tables.js"; +import { + initFromLocalStorage, + initFromQueryParams, + sortableTable, + storeFilterAndSort, + updateHistory, + respondToSortChange, +} from "../shared/sortable-tables.js"; import Get from "../../api/model/category/get.js"; window.enableDates = false; @@ -85,7 +92,10 @@ let index = function () { }, downloadObjects() { this.sortableTable.loadingNewSort = true; - let sort = "asc" === this.sortableTable.sortDirection ? this.sortableTable.sortColumn : "-" + this.sortableTable.sortColumn; + let sort = + "asc" === this.sortableTable.sortDirection + ? this.sortableTable.sortColumn + : "-" + this.sortableTable.sortColumn; this.convertToPrimary = window.store.get("convert_to_primary"); let locale = window.store.get("locale"); new Get() diff --git a/resources/assets/v3/js/pages/shared/sortable-tables.js b/resources/assets/v3/js/pages/shared/sortable-tables.js index 5cb68aa99c..084c2defb3 100644 --- a/resources/assets/v3/js/pages/shared/sortable-tables.js +++ b/resources/assets/v3/js/pages/shared/sortable-tables.js @@ -66,7 +66,9 @@ export class sortableTable { // we do this in the parent because Alpine has the watch function that is needed. this.parent.$watch("filter." + column, (newValue, oldValue) => { - console.log('filter for column "' + column + '" changed from "' + oldValue + '" to "' + newValue + '"',); + console.log( + 'filter for column "' + column + '" changed from "' + oldValue + '" to "' + newValue + '"', + ); this.fireSortChangeEvent(); if ("" !== newValue) { showSearchBox(th); @@ -234,7 +236,8 @@ export class sortableTable { } updateHeaderClasses() { - document.querySelectorAll('table[data-sort-identifier="' + this.tableId + '"] thead th[data-sort-column]') + document + .querySelectorAll('table[data-sort-identifier="' + this.tableId + '"] thead th[data-sort-column]') .forEach((th) => { if (this.sortColumn === th.dataset.sortColumn) { th.classList.add("sortable_sorted");