From a1c870c9621c9b2cfa5d69a1cda8449b9186b8e0 Mon Sep 17 00:00:00 2001 From: Nicky De Maeyer Date: Tue, 7 Oct 2025 14:32:23 +0200 Subject: [PATCH] improved request and balance range date handling --- .../Autocomplete/AccountController.php | 2 +- .../Controllers/Summary/BasicController.php | 2 -- .../Autocomplete/AutocompleteRequest.php | 3 ++- app/Api/V1/Requests/Data/DateRequest.php | 2 +- .../Controllers/Account/IndexController.php | 24 ++++++++----------- .../Controllers/Chart/AccountController.php | 18 +++++++------- app/Repositories/Account/AccountTasker.php | 9 +++---- .../JsonApi/Enrichments/AccountEnrichment.php | 6 +++-- app/Support/Request/ConvertsDataTypes.php | 16 ++++++------- app/Support/Steam.php | 12 ++++++++-- 10 files changed, 51 insertions(+), 43 deletions(-) diff --git a/app/Api/V1/Controllers/Autocomplete/AccountController.php b/app/Api/V1/Controllers/Autocomplete/AccountController.php index c3f7cd9618..7ab92ca47b 100644 --- a/app/Api/V1/Controllers/Autocomplete/AccountController.php +++ b/app/Api/V1/Controllers/Autocomplete/AccountController.php @@ -84,7 +84,7 @@ class AccountController extends Controller $data = $request->getData(); $types = $data['types']; $query = $data['query']; - $date = $data['date'] ?? today(config('app.timezone')); + $date = $data['date']; $return = []; $timer = Timer::getInstance(); $timer->start(sprintf('AC accounts "%s"', $query)); diff --git a/app/Api/V1/Controllers/Summary/BasicController.php b/app/Api/V1/Controllers/Summary/BasicController.php index 6b2acc86eb..64c39e5766 100644 --- a/app/Api/V1/Controllers/Summary/BasicController.php +++ b/app/Api/V1/Controllers/Summary/BasicController.php @@ -589,8 +589,6 @@ class BasicController extends Controller private function getNetWorthInfo(Carbon $end): array { - $end->endOfDay(); - /** @var User $user */ $user = auth()->user(); Log::debug(sprintf('getNetWorthInfo up until "%s".', $end->format('Y-m-d H:i:s'))); diff --git a/app/Api/V1/Requests/Autocomplete/AutocompleteRequest.php b/app/Api/V1/Requests/Autocomplete/AutocompleteRequest.php index 566624b060..6e8432ef45 100644 --- a/app/Api/V1/Requests/Autocomplete/AutocompleteRequest.php +++ b/app/Api/V1/Requests/Autocomplete/AutocompleteRequest.php @@ -48,10 +48,11 @@ class AutocompleteRequest extends FormRequest // remove 'initial balance' from allowed types. its internal $array = array_diff($array, [AccountTypeEnum::INITIAL_BALANCE->value, AccountTypeEnum::RECONCILIATION->value]); + $date = $this->getCarbonDate('date') ?? today(config('app.timezone')); return [ 'types' => $array, 'query' => $this->convertString('query'), - 'date' => $this->getCarbonDate('date'), + 'date' => $date->endOfDay(), ]; } diff --git a/app/Api/V1/Requests/Data/DateRequest.php b/app/Api/V1/Requests/Data/DateRequest.php index 7155994862..f5b37f7ca7 100644 --- a/app/Api/V1/Requests/Data/DateRequest.php +++ b/app/Api/V1/Requests/Data/DateRequest.php @@ -28,6 +28,7 @@ use FireflyIII\Exceptions\ValidationException; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\Validator; /** * Request class for end points that require date parameters. @@ -55,7 +56,6 @@ class DateRequest extends FormRequest // sanity check on dates: [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; - $start->startOfDay(); $end->endOfDay(); if ($start->diffInYears($end, true) > 5) { diff --git a/app/Http/Controllers/Account/IndexController.php b/app/Http/Controllers/Account/IndexController.php index 4f34f0dd5f..55d8f886c4 100644 --- a/app/Http/Controllers/Account/IndexController.php +++ b/app/Http/Controllers/Account/IndexController.php @@ -91,14 +91,12 @@ class IndexController extends Controller /** @var Carbon $end */ $end = clone session('end', today(config('app.timezone'))->endOfMonth()); - // #10618 go to the end of the previous day. - $start->subSecond(); - $ids = $accounts->pluck('id')->toArray(); - Log::debug(sprintf('inactive start: accountsBalancesOptimized("%s")', $start->format('Y-m-d H:i:s'))); - Log::debug(sprintf('inactive end: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s'))); - $startBalances = Steam::accountsBalancesOptimized($accounts, $start, $this->primaryCurrency, $this->convertToPrimary); - $endBalances = Steam::accountsBalancesOptimized($accounts, $end, $this->primaryCurrency, $this->convertToPrimary); + Log::debug(sprintf('inactive start: accountsBalancesInRange("%s", "%s")', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); + [ + $startBalances, + $endBalances, + ] = Steam::accountsBalancesInRange($start, $end, $accounts, $this->primaryCurrency, $this->convertToPrimary); $activities = Steam::getLastActivities($ids); @@ -169,14 +167,12 @@ class IndexController extends Controller /** @var Carbon $end */ $end = clone session('end', today(config('app.timezone'))->endOfMonth()); - // #10618 go to the end of the previous day. - $start->subSecond(); - $ids = $accounts->pluck('id')->toArray(); - Log::debug(sprintf('index start: accountsBalancesOptimized("%s")', $start->format('Y-m-d H:i:s'))); - Log::debug(sprintf('index end: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s'))); - $startBalances = Steam::accountsBalancesOptimized($accounts, $start, $this->primaryCurrency, $this->convertToPrimary); - $endBalances = Steam::accountsBalancesOptimized($accounts, $end, $this->primaryCurrency, $this->convertToPrimary); + Log::debug(sprintf('index: accountsBalancesInRange("%s", "%s")', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); + [ + $startBalances, + $endBalances, + ] = Steam::accountsBalancesInRange($start, $end, $accounts, $this->primaryCurrency, $this->convertToPrimary); $activities = Steam::getLastActivities($ids); diff --git a/app/Http/Controllers/Chart/AccountController.php b/app/Http/Controllers/Chart/AccountController.php index 3df8e35845..527f6daefb 100644 --- a/app/Http/Controllers/Chart/AccountController.php +++ b/app/Http/Controllers/Chart/AccountController.php @@ -116,10 +116,11 @@ class AccountController extends Controller $accountNames = $this->extractNames($accounts); // grab all balances - Log::debug(sprintf('expenseAccounts: accountsBalancesOptimized("%s")', $start->format('Y-m-d H:i:s'))); - Log::debug(sprintf('expenseAccounts: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s'))); - $startBalances = Steam::accountsBalancesOptimized($accounts, $start, $this->primaryCurrency, $this->convertToPrimary); - $endBalances = Steam::accountsBalancesOptimized($accounts, $end, $this->primaryCurrency, $this->convertToPrimary); + Log::debug(sprintf('expenseAccounts: accountsBalancesInRange("%s", "%s")', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); + [ + $startBalances, + $endBalances, + ] = Steam::accountsBalancesInRange($start, $end, $accounts, $this->primaryCurrency, $this->convertToPrimary); Log::debug('Done collecting balances'); // loop the accounts, then check for balance and currency info. foreach ($accounts as $account) { @@ -666,10 +667,11 @@ class AccountController extends Controller $accountNames = $this->extractNames($accounts); // grab all balances - Log::debug(sprintf('revAccounts: accountsBalancesOptimized("%s")', $start->format('Y-m-d H:i:s'))); - Log::debug(sprintf('revAccounts: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s'))); - $startBalances = Steam::accountsBalancesOptimized($accounts, $start, $this->primaryCurrency, $this->convertToPrimary); - $endBalances = Steam::accountsBalancesOptimized($accounts, $end, $this->primaryCurrency, $this->convertToPrimary); + Log::debug(sprintf('revAccounts: accountsBalancesInRange("%s", "%s")', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); + [ + $startBalances, + $endBalances, + ] = Steam::accountsBalancesInRange($start, $end, $accounts, $this->primaryCurrency, $this->convertToPrimary); // loop the accounts, then check for balance and currency info. diff --git a/app/Repositories/Account/AccountTasker.php b/app/Repositories/Account/AccountTasker.php index 63260b6643..1b0cc75be1 100644 --- a/app/Repositories/Account/AccountTasker.php +++ b/app/Repositories/Account/AccountTasker.php @@ -50,10 +50,11 @@ class AccountTasker implements AccountTaskerInterface, UserGroupInterface $yesterday = clone $start; $yesterday->subDay()->endOfDay(); // exactly up until $start but NOT including. $end->endOfDay(); // needs to be end of day to be correct. - Log::debug(sprintf('getAccountReport: accountsBalancesOptimized("%s")', $yesterday->format('Y-m-d H:i:s'))); - Log::debug(sprintf('getAccountReport: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s'))); - $startSet = Steam::accountsBalancesOptimized($accounts, $yesterday); - $endSet = Steam::accountsBalancesOptimized($accounts, $end); + Log::debug(sprintf('getAccountReport: accountsBalancesInRange("%s", "%s")', $yesterday->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); + [ + $startSet, + $endSet, + ] = Steam::accountsBalancesInRange($yesterday, $end, $accounts); Log::debug('Start of accountreport'); /** @var AccountRepositoryInterface $repository */ diff --git a/app/Support/JsonApi/Enrichments/AccountEnrichment.php b/app/Support/JsonApi/Enrichments/AccountEnrichment.php index 7aedeb6880..d89bb2c704 100644 --- a/app/Support/JsonApi/Enrichments/AccountEnrichment.php +++ b/app/Support/JsonApi/Enrichments/AccountEnrichment.php @@ -289,8 +289,10 @@ class AccountEnrichment implements EnrichmentInterface { $this->balances = Steam::accountsBalancesOptimized($this->collection, $this->getDate(), $this->primaryCurrency, $this->convertToPrimary); if ($this->start instanceof Carbon && $this->end instanceof Carbon) { - $this->startBalances = Steam::accountsBalancesOptimized($this->collection, $this->start, $this->primaryCurrency, $this->convertToPrimary); - $this->endBalances = Steam::accountsBalancesOptimized($this->collection, $this->end, $this->primaryCurrency, $this->convertToPrimary); + [ + $this->startBalances, + $this->endBalances, + ] = Steam::accountsBalancesInRange($this->start, $this->end, $this->collection, $this->primaryCurrency, $this->convertToPrimary); } } diff --git a/app/Support/Request/ConvertsDataTypes.php b/app/Support/Request/ConvertsDataTypes.php index a57c45a320..f8cb76378f 100644 --- a/app/Support/Request/ConvertsDataTypes.php +++ b/app/Support/Request/ConvertsDataTypes.php @@ -402,21 +402,21 @@ trait ConvertsDataTypes */ protected function getCarbonDate(string $field): ?Carbon { - $result = null; + $data = (string)$this->get($field); + Log::debug(sprintf('Date string is "%s"', $data)); - Log::debug(sprintf('Date string is "%s"', (string)$this->get($field))); + if ('' === $data) { + return null; + } try { - $result = '' !== (string)$this->get($field) ? new Carbon((string)$this->get($field), config('app.timezone')) : null; + return new Carbon($data, config('app.timezone')); } catch (InvalidFormatException) { // @ignoreException - Log::debug(sprintf('Exception when parsing date "%s".', $this->get($field))); - } - if (!$result instanceof Carbon) { - Log::debug(sprintf('Exception when parsing date "%s".', $this->get($field))); + Log::debug(sprintf('Exception when parsing date "%s".', $data)); } - return $result; + return null; } /** diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 10a8061153..0e4ddd661b 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -49,7 +49,7 @@ use function Safe\preg_replace; */ class Steam { - public function accountsBalancesOptimized(Collection $accounts, Carbon $date, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null): array + public function accountsBalancesOptimized(Collection $accounts, Carbon $date, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null, bool $inclusive = true): array { Log::debug(sprintf('accountsBalancesOptimized: Called for %d account(s) with date/time "%s"', $accounts->count(), $date->toIso8601String())); $result = []; @@ -61,7 +61,7 @@ class Steam $arrayOfSums = Transaction::whereIn('account_id', $accounts->pluck('id')->toArray()) ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id') - ->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s')) + ->where('transaction_journals.date', $inclusive ? '<=': '<', $date->format('Y-m-d H:i:s')) ->groupBy(['transactions.account_id', 'transaction_currencies.code']) ->get(['transactions.account_id', 'transaction_currencies.code', DB::raw('SUM(transactions.amount) as sum_of_amount')])->toArray() ; @@ -125,6 +125,14 @@ class Steam return $result; } + public function accountsBalancesInRange(Carbon $start, Carbon $end, Collection $accounts, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null): array + { + return [ + $this->accountsBalancesOptimized($accounts, $start, $primary, $convertToPrimary, inclusive: false), + $this->accountsBalancesOptimized($accounts, $end, $primary, $convertToPrimary), + ]; + } + /** * https://stackoverflow.com/questions/1642614/how-to-ceil-floor-and-round-bcmath-numbers */