From e5c409a8fc73e36bf79069c66928c4564d552de8 Mon Sep 17 00:00:00 2001 From: JC5 Date: Sat, 13 Sep 2025 18:52:04 +0200 Subject: [PATCH 01/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-09-13?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/firefly.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/firefly.php b/config/firefly.php index c2af8616ae..eecaf07c61 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -78,8 +78,8 @@ return [ 'running_balance_column' => env('USE_RUNNING_BALANCE', false), // see cer.php for exchange rates feature flag. ], - 'version' => '6.4.0', - 'build_time' => 1757781366, + 'version' => 'develop/2025-09-13', + 'build_time' => 1757782204, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 26, From 30df6684cb2921a1435f06262a1de7ce66c0d23d Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 14 Sep 2025 07:45:54 +0200 Subject: [PATCH 02/91] Fix another missing filter for #10803 --- .../Budget/OperationsRepository.php | 2 +- .../Enrichments/BudgetLimitEnrichment.php | 52 ++++++++++--------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/app/Repositories/Budget/OperationsRepository.php b/app/Repositories/Budget/OperationsRepository.php index 211c4fa1f5..badde3a9fb 100644 --- a/app/Repositories/Budget/OperationsRepository.php +++ b/app/Repositories/Budget/OperationsRepository.php @@ -314,7 +314,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn #[Override] public function collectExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null, ?TransactionCurrency $currency = null): array { - Log::debug(sprintf('Start of %s(date, date, array, array, "%s").', __METHOD__, $currency?->code)); + Log::debug(sprintf('Start of %s(%s, %s, array, array, "%s").', __METHOD__, $start->toW3cString(), $end->toW3cString(), $currency?->code)); // 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. diff --git a/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php b/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php index 87e8b7a0a8..c0af1e7735 100644 --- a/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php +++ b/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php @@ -40,18 +40,18 @@ use Illuminate\Support\Facades\Log; class BudgetLimitEnrichment implements EnrichmentInterface { - private User $user; - private UserGroup $userGroup; // @phpstan-ignore-line - private Collection $collection; - private array $ids = []; - private array $notes = []; - private Carbon $start; - private Carbon $end; - private array $expenses = []; - private array $pcExpenses = []; - private array $currencyIds = []; - private array $currencies = []; - private bool $convertToPrimary = true; + private User $user; + private UserGroup $userGroup; // @phpstan-ignore-line + private Collection $collection; + private array $ids = []; + private array $notes = []; + private Carbon $start; + private Carbon $end; + private array $expenses = []; + private array $pcExpenses = []; + private array $currencyIds = []; + private array $currencies = []; + private bool $convertToPrimary = true; private readonly TransactionCurrency $primaryCurrency; public function __construct() @@ -95,8 +95,8 @@ class BudgetLimitEnrichment implements EnrichmentInterface private function collectIds(): void { - $this->start = $this->collection->min('start_date') ?? Carbon::now()->startOfMonth(); - $this->end = $this->collection->max('end_date') ?? Carbon::now()->endOfMonth(); + $this->start = $this->collection->min('start_date') ?? Carbon::now()->startOfMonth(); + $this->end = $this->collection->max('end_date') ?? Carbon::now()->endOfMonth(); /** @var BudgetLimit $limit */ foreach ($this->collection as $limit) { @@ -113,10 +113,9 @@ class BudgetLimitEnrichment implements EnrichmentInterface private function collectNotes(): void { $notes = Note::query()->whereIn('noteable_id', $this->ids) - ->whereNotNull('notes.text') - ->where('notes.text', '!=', '') - ->where('noteable_type', BudgetLimit::class)->get(['notes.noteable_id', 'notes.text'])->toArray() - ; + ->whereNotNull('notes.text') + ->where('notes.text', '!=', '') + ->where('noteable_type', BudgetLimit::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); foreach ($notes as $note) { $this->notes[(int)$note['noteable_id']] = (string)$note['text']; } @@ -145,18 +144,19 @@ class BudgetLimitEnrichment implements EnrichmentInterface private function collectBudgets(): void { - $budgetIds = $this->collection->pluck('budget_id')->unique()->toArray(); - $budgets = Budget::whereIn('id', $budgetIds)->get(); + $budgetIds = $this->collection->pluck('budget_id')->unique()->toArray(); + $budgets = Budget::whereIn('id', $budgetIds)->get(); $repository = app(OperationsRepository::class); $repository->setUser($this->user); - $expenses = $repository->collectExpenses($this->start, $this->end, null, $budgets, null); + $expenses = $repository->collectExpenses($this->start, $this->end, null, $budgets, null); /** @var BudgetLimit $budgetLimit */ foreach ($this->collection as $budgetLimit) { + Log::debug(sprintf('Filtering expenses for budget limit #%d (budget #%d)', $budgetLimit->id, $budgetLimit->budget_id)); $id = (int)$budgetLimit->id; $filteredExpenses = $this->filterToBudget($expenses, $budgetLimit->budget_id); - $filteredExpenses = $repository->sumCollectedExpenses($expenses, $budgetLimit->start_date, $budgetLimit->end_date, $budgetLimit->transactionCurrency, false); + $filteredExpenses = $repository->sumCollectedExpenses($filteredExpenses, $budgetLimit->start_date, $budgetLimit->end_date, $budgetLimit->transactionCurrency, false); $this->expenses[$id] = array_values($filteredExpenses); if (true === $this->convertToPrimary && $budgetLimit->transactionCurrency->id !== $this->primaryCurrency->id) { @@ -180,13 +180,13 @@ class BudgetLimitEnrichment implements EnrichmentInterface private function stringifyIds(): void { - $this->expenses = array_map(fn ($first) => array_map(function ($second) { + $this->expenses = array_map(fn($first) => array_map(function ($second) { $second['currency_id'] = (string)($second['currency_id'] ?? 0); return $second; }, $first), $this->expenses); - $this->pcExpenses = array_map(fn ($first) => array_map(function ($second) { + $this->pcExpenses = array_map(fn($first) => array_map(function ($second) { $second['currency_id'] = (string)($second['currency_id'] ?? 0); return $second; @@ -195,6 +195,8 @@ class BudgetLimitEnrichment implements EnrichmentInterface private function filterToBudget(array $expenses, int $budget): array { - return array_filter($expenses, fn (array $item) => (int)$item['budget_id'] === $budget); + $result = array_filter($expenses, fn(array $item) => (int)$item['budget_id'] === $budget); + Log::debug(sprintf('filterToBudget for budget #%d, from %d to %d items', $budget, count($expenses), count($result))); + return $result; } } From fad016f92f62a28d21119cc496dd13ccfbeeb156 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 14 Sep 2025 07:46:46 +0200 Subject: [PATCH 03/91] Update changelog. --- changelog.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index b37af58197..93c2a96ae5 100644 --- a/changelog.md +++ b/changelog.md @@ -3,7 +3,13 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## 6.4.0 - 2025-09-19 +## 6.4.1 - 2025-09-15 + +### Fixed + +- Fixed a missing filter from [issue 10803](https://github.com/firefly-iii/firefly-iii/issues/10803). + +## 6.4.0 - 2025-09-14 ### Added From 9e6f9d16e4936d6f24eda419af2beb3df6a116a7 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 14 Sep 2025 08:55:08 +0200 Subject: [PATCH 04/91] Move observers to attributes. --- app/Models/Account.php | 3 ++ app/Models/Attachment.php | 3 ++ app/Models/AutoBudget.php | 24 ++++++---- app/Models/AvailableBudget.php | 21 +++++---- app/Models/Bill.php | 25 +++++----- app/Models/Budget.php | 3 ++ app/Models/BudgetLimit.php | 18 +++---- app/Models/Category.php | 4 ++ app/Models/PiggyBank.php | 20 ++++---- app/Models/PiggyBankEvent.php | 11 +++-- app/Models/Recurrence.php | 13 ++++-- app/Models/RecurrenceTransaction.php | 19 ++++---- app/Models/Rule.php | 15 +++--- app/Models/RuleGroup.php | 4 ++ app/Models/Tag.php | 6 ++- app/Models/Transaction.php | 4 ++ app/Models/TransactionGroup.php | 4 ++ app/Models/TransactionJournal.php | 5 ++ app/Models/Webhook.php | 4 ++ app/Models/WebhookMessage.php | 13 ++++-- app/Providers/EventServiceProvider.php | 65 -------------------------- 21 files changed, 144 insertions(+), 140 deletions(-) diff --git a/app/Models/Account.php b/app/Models/Account.php index 683b4a2973..eef5d6fcd5 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -23,6 +23,8 @@ declare(strict_types=1); namespace FireflyIII\Models; +use FireflyIII\Handlers\Observer\AccountObserver; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Attributes\Scope; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; @@ -40,6 +42,7 @@ use Illuminate\Database\Eloquent\Relations\MorphToMany; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +#[ObservedBy([AccountObserver::class])] class Account extends Model { use HasFactory; diff --git a/app/Models/Attachment.php b/app/Models/Attachment.php index 2287666db1..ed5a20bab3 100644 --- a/app/Models/Attachment.php +++ b/app/Models/Attachment.php @@ -23,9 +23,11 @@ declare(strict_types=1); namespace FireflyIII\Models; +use FireflyIII\Handlers\Observer\AttachmentObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -34,6 +36,7 @@ use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +#[ObservedBy([AttachmentObserver::class])] class Attachment extends Model { use ReturnsIntegerIdTrait; diff --git a/app/Models/AutoBudget.php b/app/Models/AutoBudget.php index 7f53584616..d1aa6475c9 100644 --- a/app/Models/AutoBudget.php +++ b/app/Models/AutoBudget.php @@ -25,31 +25,37 @@ declare(strict_types=1); namespace FireflyIII\Models; use Deprecated; +use FireflyIII\Handlers\Observer\AutoBudgetObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; +#[ObservedBy([AutoBudgetObserver::class])] class AutoBudget extends Model { use ReturnsIntegerIdTrait; use SoftDeletes; - #[Deprecated] /** @deprecated */ + #[Deprecated] + /** @deprecated */ public const int AUTO_BUDGET_ADJUSTED = 3; - #[Deprecated] /** @deprecated */ - public const int AUTO_BUDGET_RESET = 1; + #[Deprecated] + /** @deprecated */ + public const int AUTO_BUDGET_RESET = 1; - #[Deprecated] /** @deprecated */ + #[Deprecated] + /** @deprecated */ public const int AUTO_BUDGET_ROLLOVER = 2; protected $casts - = [ + = [ 'amount' => 'string', 'native_amount' => 'string', ]; - protected $fillable = ['budget_id', 'amount', 'period', 'native_amount']; + protected $fillable = ['budget_id', 'amount', 'period', 'native_amount']; public function budget(): BelongsTo { @@ -64,14 +70,14 @@ class AutoBudget extends Model protected function amount(): Attribute { return Attribute::make( - get: static fn ($value) => (string) $value, + get: static fn($value) => (string)$value, ); } protected function budgetId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } @@ -85,7 +91,7 @@ class AutoBudget extends Model protected function transactionCurrencyId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } } diff --git a/app/Models/AvailableBudget.php b/app/Models/AvailableBudget.php index 576864a2ea..f26f7f33d7 100644 --- a/app/Models/AvailableBudget.php +++ b/app/Models/AvailableBudget.php @@ -24,15 +24,18 @@ declare(strict_types=1); namespace FireflyIII\Models; use Carbon\Carbon; +use FireflyIII\Handlers\Observer\AvailableBudgetObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +#[ObservedBy([AvailableBudgetObserver::class])] class AvailableBudget extends Model { use ReturnsIntegerIdTrait; @@ -49,13 +52,13 @@ class AvailableBudget extends Model public static function routeBinder(string $value): self { if (auth()->check()) { - $availableBudgetId = (int) $value; + $availableBudgetId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|AvailableBudget $availableBudget */ - $availableBudget = $user->availableBudgets()->find($availableBudgetId); + $availableBudget = $user->availableBudgets()->find($availableBudgetId); if (null !== $availableBudget) { return $availableBudget; } @@ -77,30 +80,30 @@ class AvailableBudget extends Model protected function amount(): Attribute { return Attribute::make( - get: static fn ($value) => (string) $value, + get: static fn($value) => (string)$value, ); } protected function endDate(): Attribute { return Attribute::make( - get: fn (string $value) => Carbon::parse($value), - set: fn (Carbon $value) => $value->format('Y-m-d'), + get: fn(string $value) => Carbon::parse($value), + set: fn(Carbon $value) => $value->format('Y-m-d'), ); } protected function startDate(): Attribute { return Attribute::make( - get: fn (string $value) => Carbon::parse($value), - set: fn (Carbon $value) => $value->format('Y-m-d'), + get: fn(string $value) => Carbon::parse($value), + set: fn(Carbon $value) => $value->format('Y-m-d'), ); } protected function transactionCurrencyId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } diff --git a/app/Models/Bill.php b/app/Models/Bill.php index a0f59bc9d4..e44d0f3098 100644 --- a/app/Models/Bill.php +++ b/app/Models/Bill.php @@ -24,9 +24,11 @@ declare(strict_types=1); namespace FireflyIII\Models; use FireflyIII\Casts\SeparateTimezoneCaster; +use FireflyIII\Handlers\Observer\BillObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -36,6 +38,7 @@ use Illuminate\Database\Eloquent\Relations\MorphToMany; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +#[ObservedBy([BillObserver::class])] class Bill extends Model { use ReturnsIntegerIdTrait; @@ -43,7 +46,7 @@ class Bill extends Model use SoftDeletes; protected $fillable - = [ + = [ 'name', 'match', 'amount_min', @@ -75,13 +78,13 @@ class Bill extends Model public static function routeBinder(string $value): self { if (auth()->check()) { - $billId = (int) $value; + $billId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|Bill $bill */ - $bill = $user->bills()->find($billId); + $bill = $user->bills()->find($billId); if (null !== $bill) { return $bill; } @@ -121,7 +124,7 @@ class Bill extends Model */ public function setAmountMaxAttribute($value): void { - $this->attributes['amount_max'] = (string) $value; + $this->attributes['amount_max'] = (string)$value; } /** @@ -129,7 +132,7 @@ class Bill extends Model */ public function setAmountMinAttribute($value): void { - $this->attributes['amount_min'] = (string) $value; + $this->attributes['amount_min'] = (string)$value; } public function transactionCurrency(): BelongsTo @@ -148,7 +151,7 @@ class Bill extends Model protected function amountMax(): Attribute { return Attribute::make( - get: static fn ($value) => (string) $value, + get: static fn($value) => (string)$value, ); } @@ -158,14 +161,14 @@ class Bill extends Model protected function amountMin(): Attribute { return Attribute::make( - get: static fn ($value) => (string) $value, + get: static fn($value) => (string)$value, ); } protected function order(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } @@ -175,14 +178,14 @@ class Bill extends Model protected function skip(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } protected function transactionCurrencyId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } diff --git a/app/Models/Budget.php b/app/Models/Budget.php index c5c1c29d46..086764d66d 100644 --- a/app/Models/Budget.php +++ b/app/Models/Budget.php @@ -23,9 +23,11 @@ declare(strict_types=1); namespace FireflyIII\Models; +use FireflyIII\Handlers\Observer\BudgetObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -35,6 +37,7 @@ use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +#[ObservedBy([BudgetObserver::class])] class Budget extends Model { use ReturnsIntegerIdTrait; diff --git a/app/Models/BudgetLimit.php b/app/Models/BudgetLimit.php index e7270fc7b0..247cf24ad4 100644 --- a/app/Models/BudgetLimit.php +++ b/app/Models/BudgetLimit.php @@ -24,13 +24,16 @@ declare(strict_types=1); namespace FireflyIII\Models; use FireflyIII\Casts\SeparateTimezoneCaster; +use FireflyIII\Handlers\Observer\BudgetLimitObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphMany; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +#[ObservedBy([BudgetLimitObserver::class])] class BudgetLimit extends Model { use ReturnsIntegerIdTrait; @@ -45,12 +48,11 @@ class BudgetLimit extends Model public static function routeBinder(string $value): self { if (auth()->check()) { - $budgetLimitId = (int) $value; + $budgetLimitId = (int)$value; $budgetLimit = self::where('budget_limits.id', $budgetLimitId) - ->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') - ->where('budgets.user_id', auth()->user()->id) - ->first(['budget_limits.*']) - ; + ->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') + ->where('budgets.user_id', auth()->user()->id) + ->first(['budget_limits.*']); if (null !== $budgetLimit) { return $budgetLimit; } @@ -83,21 +85,21 @@ class BudgetLimit extends Model protected function amount(): Attribute { return Attribute::make( - get: static fn ($value) => (string) $value, + get: static fn($value) => (string)$value, ); } protected function budgetId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } protected function transactionCurrencyId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } diff --git a/app/Models/Category.php b/app/Models/Category.php index 3e50f448c6..1f961f561d 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -24,9 +24,12 @@ declare(strict_types=1); namespace FireflyIII\Models; +use FireflyIII\Handlers\Observer\AccountObserver; +use FireflyIII\Handlers\Observer\CategoryObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; @@ -34,6 +37,7 @@ use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +#[ObservedBy([CategoryObserver::class])] class Category extends Model { use ReturnsIntegerIdTrait; diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index 08d5a2563d..ae161b8c93 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -23,7 +23,9 @@ declare(strict_types=1); namespace FireflyIII\Models; +use FireflyIII\Handlers\Observer\PiggyBankObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -34,6 +36,7 @@ use Illuminate\Database\Eloquent\Relations\MorphToMany; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +#[ObservedBy([PiggyBankObserver::class])] class PiggyBank extends Model { use ReturnsIntegerIdTrait; @@ -49,12 +52,11 @@ class PiggyBank extends Model public static function routeBinder(string $value): self { if (auth()->check()) { - $piggyBankId = (int) $value; + $piggyBankId = (int)$value; $piggyBank = self::where('piggy_banks.id', $piggyBankId) - ->leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id') - ->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id') - ->where('accounts.user_id', auth()->user()->id)->first(['piggy_banks.*']) - ; + ->leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id') + ->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id') + ->where('accounts.user_id', auth()->user()->id)->first(['piggy_banks.*']); if (null !== $piggyBank) { return $piggyBank; } @@ -109,7 +111,7 @@ class PiggyBank extends Model */ public function setTargetAmountAttribute($value): void { - $this->attributes['target_amount'] = (string) $value; + $this->attributes['target_amount'] = (string)$value; } public function transactionCurrency(): BelongsTo @@ -120,14 +122,14 @@ class PiggyBank extends Model protected function accountId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } protected function order(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } @@ -137,7 +139,7 @@ class PiggyBank extends Model protected function targetAmount(): Attribute { return Attribute::make( - get: static fn ($value) => (string) $value, + get: static fn($value) => (string)$value, ); } diff --git a/app/Models/PiggyBankEvent.php b/app/Models/PiggyBankEvent.php index 733792ffeb..9599c2f027 100644 --- a/app/Models/PiggyBankEvent.php +++ b/app/Models/PiggyBankEvent.php @@ -24,18 +24,21 @@ declare(strict_types=1); namespace FireflyIII\Models; use FireflyIII\Casts\SeparateTimezoneCaster; +use FireflyIII\Handlers\Observer\PiggyBankEventObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +#[ObservedBy([PiggyBankEventObserver::class])] class PiggyBankEvent extends Model { use ReturnsIntegerIdTrait; protected $fillable = ['piggy_bank_id', 'transaction_journal_id', 'date', 'date_tz', 'amount', 'native_amount']; - protected $hidden = ['amount_encrypted']; + protected $hidden = ['amount_encrypted']; public function piggyBank(): BelongsTo { @@ -47,7 +50,7 @@ class PiggyBankEvent extends Model */ public function setAmountAttribute($value): void { - $this->attributes['amount'] = (string) $value; + $this->attributes['amount'] = (string)$value; } public function transactionJournal(): BelongsTo @@ -61,14 +64,14 @@ class PiggyBankEvent extends Model protected function amount(): Attribute { return Attribute::make( - get: static fn ($value) => (string) $value, + get: static fn($value) => (string)$value, ); } protected function piggyBankId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } diff --git a/app/Models/Recurrence.php b/app/Models/Recurrence.php index 4e369fca27..78051e6bc6 100644 --- a/app/Models/Recurrence.php +++ b/app/Models/Recurrence.php @@ -25,9 +25,11 @@ declare(strict_types=1); namespace FireflyIII\Models; use FireflyIII\Casts\SeparateTimezoneCaster; +use FireflyIII\Handlers\Observer\RecurrenceObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -36,6 +38,7 @@ use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +#[ObservedBy([RecurrenceObserver::class])] class Recurrence extends Model { use ReturnsIntegerIdTrait; @@ -43,7 +46,7 @@ class Recurrence extends Model use SoftDeletes; protected $fillable - = ['user_id', 'user_group_id', 'transaction_type_id', 'title', 'description', 'first_date', 'first_date_tz', 'repeat_until', 'repeat_until_tz', 'latest_date', 'latest_date_tz', 'repetitions', 'apply_rules', 'active']; + = ['user_id', 'user_group_id', 'transaction_type_id', 'title', 'description', 'first_date', 'first_date_tz', 'repeat_until', 'repeat_until_tz', 'latest_date', 'latest_date_tz', 'repetitions', 'apply_rules', 'active']; protected $table = 'recurrences'; @@ -55,13 +58,13 @@ class Recurrence extends Model public static function routeBinder(string $value): self { if (auth()->check()) { - $recurrenceId = (int) $value; + $recurrenceId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|Recurrence $recurrence */ - $recurrence = $user->recurrences()->find($recurrenceId); + $recurrence = $user->recurrences()->find($recurrenceId); if (null !== $recurrence) { return $recurrence; } @@ -116,7 +119,7 @@ class Recurrence extends Model protected function transactionTypeId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } diff --git a/app/Models/RecurrenceTransaction.php b/app/Models/RecurrenceTransaction.php index 2c17d52ebb..245e19865f 100644 --- a/app/Models/RecurrenceTransaction.php +++ b/app/Models/RecurrenceTransaction.php @@ -24,20 +24,23 @@ declare(strict_types=1); namespace FireflyIII\Models; +use FireflyIII\Handlers\Observer\RecurrenceTransactionObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; +#[ObservedBy([RecurrenceTransactionObserver::class])] class RecurrenceTransaction extends Model { use ReturnsIntegerIdTrait; use SoftDeletes; protected $fillable - = [ + = [ 'recurrence_id', 'transaction_currency_id', 'foreign_currency_id', @@ -88,49 +91,49 @@ class RecurrenceTransaction extends Model protected function amount(): Attribute { return Attribute::make( - get: static fn ($value) => (string) $value, + get: static fn($value) => (string)$value, ); } protected function destinationId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } protected function foreignAmount(): Attribute { return Attribute::make( - get: static fn ($value) => (string) $value, + get: static fn($value) => (string)$value, ); } protected function recurrenceId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } protected function sourceId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } protected function transactionCurrencyId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } protected function userId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } diff --git a/app/Models/Rule.php b/app/Models/Rule.php index 5308eae4d5..78f2906ccb 100644 --- a/app/Models/Rule.php +++ b/app/Models/Rule.php @@ -23,9 +23,11 @@ declare(strict_types=1); namespace FireflyIII\Models; +use FireflyIII\Handlers\Observer\RuleObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -33,6 +35,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +#[ObservedBy([RuleObserver::class])] class Rule extends Model { use ReturnsIntegerIdTrait; @@ -49,13 +52,13 @@ class Rule extends Model public static function routeBinder(string $value): self { if (auth()->check()) { - $ruleId = (int) $value; + $ruleId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|Rule $rule */ - $rule = $user->rules()->find($ruleId); + $rule = $user->rules()->find($ruleId); if (null !== $rule) { return $rule; } @@ -86,7 +89,7 @@ class Rule extends Model protected function description(): Attribute { - return Attribute::make(set: fn ($value) => ['description' => e($value)]); + return Attribute::make(set: fn($value) => ['description' => e($value)]); } public function userGroup(): BelongsTo @@ -97,14 +100,14 @@ class Rule extends Model protected function order(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } protected function ruleGroupId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } diff --git a/app/Models/RuleGroup.php b/app/Models/RuleGroup.php index 1a25031a7e..48b96e1a67 100644 --- a/app/Models/RuleGroup.php +++ b/app/Models/RuleGroup.php @@ -23,9 +23,12 @@ declare(strict_types=1); namespace FireflyIII\Models; +use FireflyIII\Handlers\Observer\AccountObserver; +use FireflyIII\Handlers\Observer\RuleGroupObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -33,6 +36,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +#[ObservedBy([RuleGroupObserver::class])] class RuleGroup extends Model { use ReturnsIntegerIdTrait; diff --git a/app/Models/Tag.php b/app/Models/Tag.php index 3af4799730..df1082bc09 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -24,9 +24,12 @@ declare(strict_types=1); namespace FireflyIII\Models; use FireflyIII\Casts\SeparateTimezoneCaster; +use FireflyIII\Handlers\Observer\AccountObserver; +use FireflyIII\Handlers\Observer\TagObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; @@ -34,6 +37,7 @@ use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +#[ObservedBy([TagObserver::class])] class Tag extends Model { use ReturnsIntegerIdTrait; @@ -42,7 +46,7 @@ class Tag extends Model protected $fillable = ['user_id', 'user_group_id', 'tag', 'date', 'date_tz', 'description', 'tag_mode']; - protected $hidden = ['zoomLevel', 'latitude', 'longitude']; + protected $hidden = ['zoomLevel', 'zoom_level', 'latitude', 'longitude']; /** * Route binder. Converts the key in the URL to the specified object (or throw 404). diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index a563e3656f..342a2f6b0d 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -23,6 +23,9 @@ declare(strict_types=1); namespace FireflyIII\Models; +use FireflyIII\Handlers\Observer\AccountObserver; +use FireflyIII\Handlers\Observer\TransactionObserver; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Attributes\Scope; use Carbon\Carbon; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; @@ -34,6 +37,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\SoftDeletes; +#[ObservedBy([TransactionObserver::class])] class Transaction extends Model { use HasFactory; diff --git a/app/Models/TransactionGroup.php b/app/Models/TransactionGroup.php index a09af2b231..a13c0008ad 100644 --- a/app/Models/TransactionGroup.php +++ b/app/Models/TransactionGroup.php @@ -23,15 +23,19 @@ declare(strict_types=1); namespace FireflyIII\Models; +use FireflyIII\Handlers\Observer\AccountObserver; +use FireflyIII\Handlers\Observer\TransactionGroupObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +#[ObservedBy([TransactionGroupObserver::class])] class TransactionGroup extends Model { use ReturnsIntegerIdTrait; diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index 609d7dc353..72f2ba70f2 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -23,6 +23,9 @@ declare(strict_types=1); namespace FireflyIII\Models; +use FireflyIII\Handlers\Observer\AccountObserver; +use FireflyIII\Handlers\Observer\TransactionJournalObserver; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Attributes\Scope; use Carbon\Carbon; use FireflyIII\Casts\SeparateTimezoneCaster; @@ -46,6 +49,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method EloquentBuilder|static after() * @method static EloquentBuilder|static query() */ + +#[ObservedBy([TransactionJournalObserver::class])] class TransactionJournal extends Model { use HasFactory; diff --git a/app/Models/Webhook.php b/app/Models/Webhook.php index a836b7fad6..797ca82876 100644 --- a/app/Models/Webhook.php +++ b/app/Models/Webhook.php @@ -27,9 +27,12 @@ namespace FireflyIII\Models; use FireflyIII\Enums\WebhookDelivery as WebhookDeliveryEnum; use FireflyIII\Enums\WebhookResponse as WebhookResponseEnum; use FireflyIII\Enums\WebhookTrigger as WebhookTriggerEnum; +use FireflyIII\Handlers\Observer\AccountObserver; +use FireflyIII\Handlers\Observer\WebhookObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; @@ -37,6 +40,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +#[ObservedBy([WebhookObserver::class])] class Webhook extends Model { use ReturnsIntegerIdTrait; diff --git a/app/Models/WebhookMessage.php b/app/Models/WebhookMessage.php index 77d6a0642c..c1014d60aa 100644 --- a/app/Models/WebhookMessage.php +++ b/app/Models/WebhookMessage.php @@ -24,14 +24,17 @@ declare(strict_types=1); namespace FireflyIII\Models; +use FireflyIII\Handlers\Observer\WebhookMessageObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\User; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +#[ObservedBy([WebhookMessageObserver::class])] class WebhookMessage extends Model { use ReturnsIntegerIdTrait; @@ -44,13 +47,13 @@ class WebhookMessage extends Model public static function routeBinder(string $value): self { if (auth()->check()) { - $messageId = (int) $value; + $messageId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|WebhookMessage $message */ - $message = self::find($messageId); + $message = self::find($messageId); if (null !== $message && $message->webhook->user_id === $user->id) { return $message; } @@ -75,14 +78,14 @@ class WebhookMessage extends Model protected function sent(): Attribute { return Attribute::make( - get: static fn ($value) => (bool) $value, + get: static fn($value) => (bool)$value, ); } protected function webhookId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 5cff75ae42..a63903a280 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -57,46 +57,6 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\UpdatedAccount; use FireflyIII\Events\UpdatedTransactionGroup; use FireflyIII\Events\UserChangedEmail; -use FireflyIII\Handlers\Observer\AccountObserver; -use FireflyIII\Handlers\Observer\AttachmentObserver; -use FireflyIII\Handlers\Observer\AutoBudgetObserver; -use FireflyIII\Handlers\Observer\AvailableBudgetObserver; -use FireflyIII\Handlers\Observer\BillObserver; -use FireflyIII\Handlers\Observer\BudgetLimitObserver; -use FireflyIII\Handlers\Observer\BudgetObserver; -use FireflyIII\Handlers\Observer\CategoryObserver; -use FireflyIII\Handlers\Observer\PiggyBankEventObserver; -use FireflyIII\Handlers\Observer\PiggyBankObserver; -use FireflyIII\Handlers\Observer\RecurrenceObserver; -use FireflyIII\Handlers\Observer\RecurrenceTransactionObserver; -use FireflyIII\Handlers\Observer\RuleGroupObserver; -use FireflyIII\Handlers\Observer\RuleObserver; -use FireflyIII\Handlers\Observer\TagObserver; -use FireflyIII\Handlers\Observer\TransactionGroupObserver; -use FireflyIII\Handlers\Observer\TransactionJournalObserver; -use FireflyIII\Handlers\Observer\TransactionObserver; -use FireflyIII\Handlers\Observer\WebhookMessageObserver; -use FireflyIII\Handlers\Observer\WebhookObserver; -use FireflyIII\Models\Account; -use FireflyIII\Models\Attachment; -use FireflyIII\Models\AutoBudget; -use FireflyIII\Models\AvailableBudget; -use FireflyIII\Models\Bill; -use FireflyIII\Models\Budget; -use FireflyIII\Models\BudgetLimit; -use FireflyIII\Models\Category; -use FireflyIII\Models\PiggyBank; -use FireflyIII\Models\PiggyBankEvent; -use FireflyIII\Models\Recurrence; -use FireflyIII\Models\RecurrenceTransaction; -use FireflyIII\Models\Rule; -use FireflyIII\Models\RuleGroup; -use FireflyIII\Models\Tag; -use FireflyIII\Models\Transaction; -use FireflyIII\Models\TransactionGroup; -use FireflyIII\Models\TransactionJournal; -use FireflyIII\Models\Webhook; -use FireflyIII\Models\WebhookMessage; use Illuminate\Auth\Events\Login; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Laravel\Passport\Events\AccessTokenCreated; @@ -258,30 +218,5 @@ class EventServiceProvider extends ServiceProvider #[Override] public function boot(): void { - $this->registerObservers(); - } - - private function registerObservers(): void - { - Attachment::observe(new AttachmentObserver()); - Account::observe(new AccountObserver()); - AutoBudget::observe(new AutoBudgetObserver()); - AvailableBudget::observe(new AvailableBudgetObserver()); - Bill::observe(new BillObserver()); - Budget::observe(new BudgetObserver()); - BudgetLimit::observe(new BudgetLimitObserver()); - Category::observe(new CategoryObserver()); - PiggyBank::observe(new PiggyBankObserver()); - PiggyBankEvent::observe(new PiggyBankEventObserver()); - Recurrence::observe(new RecurrenceObserver()); - RecurrenceTransaction::observe(new RecurrenceTransactionObserver()); - Rule::observe(new RuleObserver()); - RuleGroup::observe(new RuleGroupObserver()); - Tag::observe(new TagObserver()); - Transaction::observe(new TransactionObserver()); - TransactionJournal::observe(new TransactionJournalObserver()); - TransactionGroup::observe(new TransactionGroupObserver()); - Webhook::observe(new WebhookObserver()); - WebhookMessage::observe(new WebhookMessageObserver()); } } From c2d3f5da16c56763b06a755191080dfa859f3394 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 14 Sep 2025 08:55:29 +0200 Subject: [PATCH 05/91] Allow budget store to have optional webhook using "fire_webhooks". --- .../Models/Budget/StoreController.php | 14 ++++---- .../Requests/Models/Budget/StoreRequest.php | 6 ++++ .../Models/Transaction/StoreRequest.php | 1 + app/Handlers/Observer/BudgetObserver.php | 34 +++++++++++-------- app/Repositories/Budget/BudgetRepository.php | 5 +++ app/Support/Request/ConvertsDataTypes.php | 6 ++++ 6 files changed, 46 insertions(+), 20 deletions(-) diff --git a/app/Api/V1/Controllers/Models/Budget/StoreController.php b/app/Api/V1/Controllers/Models/Budget/StoreController.php index b6d9e85e6c..3e4954f474 100644 --- a/app/Api/V1/Controllers/Models/Budget/StoreController.php +++ b/app/Api/V1/Controllers/Models/Budget/StoreController.php @@ -67,22 +67,24 @@ class StoreController extends Controller */ public function store(StoreRequest $request): JsonResponse { - $budget = $this->repository->store($request->getAll()); + $data = $request->getAll(); + $data['fire_webhooks'] = $data['fire_webhooks'] ?? true; + $budget = $this->repository->store($data); $budget->refresh(); - $manager = $this->getManager(); + $manager = $this->getManager(); // enrich /** @var User $admin */ - $admin = auth()->user(); - $enrichment = new BudgetEnrichment(); + $admin = auth()->user(); + $enrichment = new BudgetEnrichment(); $enrichment->setUser($admin); - $budget = $enrichment->enrichSingle($budget); + $budget = $enrichment->enrichSingle($budget); /** @var BudgetTransformer $transformer */ $transformer = app(BudgetTransformer::class); $transformer->setParameters($this->parameters); - $resource = new Item($budget, $transformer, 'budgets'); + $resource = new Item($budget, $transformer, 'budgets'); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } diff --git a/app/Api/V1/Requests/Models/Budget/StoreRequest.php b/app/Api/V1/Requests/Models/Budget/StoreRequest.php index 9a0118406d..fc17d164dd 100644 --- a/app/Api/V1/Requests/Models/Budget/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Budget/StoreRequest.php @@ -59,6 +59,9 @@ class StoreRequest extends FormRequest 'auto_budget_type' => ['auto_budget_type', 'convertString'], 'auto_budget_amount' => ['auto_budget_amount', 'convertString'], 'auto_budget_period' => ['auto_budget_period', 'convertString'], + + // webhooks + 'fire_webhooks' => ['fire_webhooks','boolean'] ]; return $this->getAllData($fields); @@ -79,6 +82,9 @@ class StoreRequest extends FormRequest 'auto_budget_type' => 'in:reset,rollover,adjusted,none', 'auto_budget_amount' => ['required_if:auto_budget_type,reset', 'required_if:auto_budget_type,rollover', 'required_if:auto_budget_type,adjusted', new IsValidPositiveAmount()], 'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly|required_if:auto_budget_type,reset|required_if:auto_budget_type,rollover|required_if:auto_budget_type,adjusted', + + // webhooks + 'fire_webhooks' => [new IsBoolean()], ]; } diff --git a/app/Api/V1/Requests/Models/Transaction/StoreRequest.php b/app/Api/V1/Requests/Models/Transaction/StoreRequest.php index a1a20fe4d1..6e85264031 100644 --- a/app/Api/V1/Requests/Models/Transaction/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Transaction/StoreRequest.php @@ -183,6 +183,7 @@ class StoreRequest extends FormRequest // basic fields for group: 'group_title' => 'min:1|max:1000|nullable', 'error_if_duplicate_hash' => [new IsBoolean()], + 'fire_webhooks' => [new IsBoolean()], 'apply_rules' => [new IsBoolean()], // location rules diff --git a/app/Handlers/Observer/BudgetObserver.php b/app/Handlers/Observer/BudgetObserver.php index d7366d3a4b..156946e917 100644 --- a/app/Handlers/Observer/BudgetObserver.php +++ b/app/Handlers/Observer/BudgetObserver.php @@ -31,6 +31,7 @@ use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface; use FireflyIII\Support\Observers\RecalculatesAvailableBudgetsTrait; +use FireflyIII\Support\Singleton\PreferencesSingleton; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; @@ -45,23 +46,28 @@ class BudgetObserver { Log::debug(sprintf('Observe "created" of budget #%d ("%s").', $budget->id, $budget->name)); - // fire event. - $user = $budget->user; + // this is a lame trick to communicate with the observer. + $singleton = PreferencesSingleton::getInstance(); - /** @var MessageGeneratorInterface $engine */ - $engine = app(MessageGeneratorInterface::class); - $engine->setUser($user); - $engine->setObjects(new Collection()->push($budget)); - $engine->setTrigger(WebhookTrigger::STORE_BUDGET); - $engine->generateMessages(); - Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__)); - event(new RequestedSendWebhookMessages()); + if (true === $singleton->getPreference('fire_webhooks_budget_create')) { + // fire event. + $user = $budget->user; + + /** @var MessageGeneratorInterface $engine */ + $engine = app(MessageGeneratorInterface::class); + $engine->setUser($user); + $engine->setObjects(new Collection()->push($budget)); + $engine->setTrigger(WebhookTrigger::STORE_BUDGET); + $engine->generateMessages(); + Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__)); + event(new RequestedSendWebhookMessages()); + } } public function updated(Budget $budget): void { Log::debug(sprintf('Observe "updated" of budget #%d ("%s").', $budget->id, $budget->name)); - $user = $budget->user; + $user = $budget->user; /** @var MessageGeneratorInterface $engine */ $engine = app(MessageGeneratorInterface::class); @@ -77,10 +83,10 @@ class BudgetObserver { Log::debug('Observe "deleting" of a budget.'); - $user = $budget->user; + $user = $budget->user; /** @var MessageGeneratorInterface $engine */ - $engine = app(MessageGeneratorInterface::class); + $engine = app(MessageGeneratorInterface::class); $engine->setUser($user); $engine->setObjects(new Collection()->push($budget)); $engine->setTrigger(WebhookTrigger::DESTROY_BUDGET); @@ -88,7 +94,7 @@ class BudgetObserver Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__)); event(new RequestedSendWebhookMessages()); - $repository = app(AttachmentRepositoryInterface::class); + $repository = app(AttachmentRepositoryInterface::class); $repository->setUser($budget->user); /** @var Attachment $attachment */ diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 3aa57328de..8c39398b69 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -44,6 +44,7 @@ use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Http\Api\ExchangeRateConverter; use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; +use FireflyIII\Support\Singleton\PreferencesSingleton; use Illuminate\Database\QueryException; use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; @@ -724,6 +725,10 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface { $order = $this->getMaxOrder(); + // this is a lame trick to communicate with the observer. + $singleton = PreferencesSingleton::getInstance(); + $singleton->setPreference('fire_webhooks_budget_create', $data['fire_webhooks'] ?? true); + try { $newBudget = Budget::create( [ diff --git a/app/Support/Request/ConvertsDataTypes.php b/app/Support/Request/ConvertsDataTypes.php index c90541b081..bc8da96efb 100644 --- a/app/Support/Request/ConvertsDataTypes.php +++ b/app/Support/Request/ConvertsDataTypes.php @@ -258,6 +258,12 @@ trait ConvertsDataTypes if ('yes' === $value) { return true; } + if ('on' === $value) { + return true; + } + if ('y' === $value) { + return true; + } if ('1' === $value) { return true; } From 935453796e3284ad1163ca056bf2ca01762dcdd2 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 14 Sep 2025 08:59:00 +0200 Subject: [PATCH 06/91] Allow budget update to have webhooks controlled with "fire_webhooks" --- .../Models/Budget/UpdateController.php | 21 +++---- .../Requests/Models/Budget/UpdateRequest.php | 6 ++ app/Handlers/Observer/BudgetObserver.php | 24 +++++--- app/Repositories/Budget/BudgetRepository.php | 56 ++++++++++--------- 4 files changed, 59 insertions(+), 48 deletions(-) diff --git a/app/Api/V1/Controllers/Models/Budget/UpdateController.php b/app/Api/V1/Controllers/Models/Budget/UpdateController.php index b6524ba738..8ec009b7e1 100644 --- a/app/Api/V1/Controllers/Models/Budget/UpdateController.php +++ b/app/Api/V1/Controllers/Models/Budget/UpdateController.php @@ -57,30 +57,25 @@ class UpdateController extends Controller ); } - /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/budgets/updateBudget - * - * Update a budget. - */ public function update(UpdateRequest $request, Budget $budget): JsonResponse { - $data = $request->getAll(); - $budget = $this->repository->update($budget, $data); - $manager = $this->getManager(); + $data = $request->getAll(); + $data['fire_webhooks'] = $data['fire_webhooks'] ?? true; + $budget = $this->repository->update($budget, $data); + $manager = $this->getManager(); // enrich /** @var User $admin */ - $admin = auth()->user(); - $enrichment = new BudgetEnrichment(); + $admin = auth()->user(); + $enrichment = new BudgetEnrichment(); $enrichment->setUser($admin); - $budget = $enrichment->enrichSingle($budget); + $budget = $enrichment->enrichSingle($budget); /** @var BudgetTransformer $transformer */ $transformer = app(BudgetTransformer::class); $transformer->setParameters($this->parameters); - $resource = new Item($budget, $transformer, 'budgets'); + $resource = new Item($budget, $transformer, 'budgets'); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } diff --git a/app/Api/V1/Requests/Models/Budget/UpdateRequest.php b/app/Api/V1/Requests/Models/Budget/UpdateRequest.php index 6eb0dc7acc..3a89dbaa15 100644 --- a/app/Api/V1/Requests/Models/Budget/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Budget/UpdateRequest.php @@ -59,6 +59,9 @@ class UpdateRequest extends FormRequest 'auto_budget_type' => ['auto_budget_type', 'convertString'], 'auto_budget_amount' => ['auto_budget_amount', 'convertString'], 'auto_budget_period' => ['auto_budget_period', 'convertString'], + + // webhooks + 'fire_webhooks' => ['fire_webhooks','boolean'] ]; $allData = $this->getAllData($fields); if (array_key_exists('auto_budget_type', $allData)) { @@ -91,6 +94,9 @@ class UpdateRequest extends FormRequest 'auto_budget_currency_code' => 'exists:transaction_currencies,code', 'auto_budget_amount' => ['nullable', new IsValidPositiveAmount()], 'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly', + + // webhooks + 'fire_webhooks' => [new IsBoolean()], ]; } diff --git a/app/Handlers/Observer/BudgetObserver.php b/app/Handlers/Observer/BudgetObserver.php index 156946e917..9030c4f955 100644 --- a/app/Handlers/Observer/BudgetObserver.php +++ b/app/Handlers/Observer/BudgetObserver.php @@ -67,16 +67,22 @@ class BudgetObserver public function updated(Budget $budget): void { Log::debug(sprintf('Observe "updated" of budget #%d ("%s").', $budget->id, $budget->name)); - $user = $budget->user; - /** @var MessageGeneratorInterface $engine */ - $engine = app(MessageGeneratorInterface::class); - $engine->setUser($user); - $engine->setObjects(new Collection()->push($budget)); - $engine->setTrigger(WebhookTrigger::UPDATE_BUDGET); - $engine->generateMessages(); - Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__)); - event(new RequestedSendWebhookMessages()); + // this is a lame trick to communicate with the observer. + $singleton = PreferencesSingleton::getInstance(); + + if (true === $singleton->getPreference('fire_webhooks_budget_update')) { + $user = $budget->user; + + /** @var MessageGeneratorInterface $engine */ + $engine = app(MessageGeneratorInterface::class); + $engine->setUser($user); + $engine->setObjects(new Collection()->push($budget)); + $engine->setTrigger(WebhookTrigger::UPDATE_BUDGET); + $engine->generateMessages(); + Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__)); + event(new RequestedSendWebhookMessages()); + } } public function deleting(Budget $budget): void diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 8c39398b69..b893334320 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -86,7 +86,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface public function budgetedInPeriod(Carbon $start, Carbon $end): array { - app('log')->debug(sprintf('Now in budgetedInPeriod("%s", "%s")', $start->format('Y-m-d'), $end->format('Y-m-d'))); + Log::debug(sprintf('Now in budgetedInPeriod("%s", "%s")', $start->format('Y-m-d'), $end->format('Y-m-d'))); $return = []; /** @var BudgetLimitRepository $limitRepository */ @@ -98,12 +98,12 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface /** @var Budget $budget */ foreach ($budgets as $budget) { - app('log')->debug(sprintf('Budget #%d: "%s"', $budget->id, $budget->name)); + Log::debug(sprintf('Budget #%d: "%s"', $budget->id, $budget->name)); $limits = $limitRepository->getBudgetLimits($budget, $start, $end); /** @var BudgetLimit $limit */ foreach ($limits as $limit) { - app('log')->debug(sprintf('Budget limit #%d', $limit->id)); + Log::debug(sprintf('Budget limit #%d', $limit->id)); $currency = $limit->transactionCurrency; $rate = $converter->getCurrencyRate($currency, $primaryCurrency, $end); $currencyCode = $currency->code; @@ -125,7 +125,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface if ($limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end)) { $return[$currencyCode]['sum'] = bcadd($return[$currencyCode]['sum'], (string) $limit->amount); $return[$currencyCode]['pc_sum'] = bcmul($rate, $return[$currencyCode]['sum']); - app('log')->debug(sprintf('Add full amount [1]: %s', $limit->amount)); + Log::debug(sprintf('Add full amount [1]: %s', $limit->amount)); continue; } @@ -133,7 +133,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface if ($start->lte($limit->start_date) && $end->gte($limit->end_date)) { $return[$currencyCode]['sum'] = bcadd($return[$currencyCode]['sum'], (string) $limit->amount); $return[$currencyCode]['pc_sum'] = bcmul($rate, $return[$currencyCode]['sum']); - app('log')->debug(sprintf('Add full amount [2]: %s', $limit->amount)); + Log::debug(sprintf('Add full amount [2]: %s', $limit->amount)); continue; } @@ -142,7 +142,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface $amount = bcmul(bcdiv((string) $limit->amount, (string) $total), (string) $days); $return[$currencyCode]['sum'] = bcadd($return[$currencyCode]['sum'], $amount); $return[$currencyCode]['pc_sum'] = bcmul($rate, $return[$currencyCode]['sum']); - app('log')->debug( + Log::debug( sprintf( 'Amount per day: %s (%s over %d days). Total amount for %d days: %s', bcdiv((string) $limit->amount, (string) $total), @@ -203,19 +203,19 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface public function budgetedInPeriodForBudget(Budget $budget, Carbon $start, Carbon $end): array { - app('log')->debug(sprintf('Now in budgetedInPeriod(#%d, "%s", "%s")', $budget->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); + Log::debug(sprintf('Now in budgetedInPeriod(#%d, "%s", "%s")', $budget->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); $return = []; /** @var BudgetLimitRepository $limitRepository */ $limitRepository = app(BudgetLimitRepository::class); $limitRepository->setUser($this->user); - app('log')->debug(sprintf('Budget #%d: "%s"', $budget->id, $budget->name)); + Log::debug(sprintf('Budget #%d: "%s"', $budget->id, $budget->name)); $limits = $limitRepository->getBudgetLimits($budget, $start, $end); /** @var BudgetLimit $limit */ foreach ($limits as $limit) { - app('log')->debug(sprintf('Budget limit #%d', $limit->id)); + Log::debug(sprintf('Budget limit #%d', $limit->id)); $currency = $limit->transactionCurrency; $return[$currency->id] ??= [ 'id' => (string) $currency->id, @@ -228,14 +228,14 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface // same period if ($limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end)) { $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], (string) $limit->amount); - app('log')->debug(sprintf('Add full amount [1]: %s', $limit->amount)); + Log::debug(sprintf('Add full amount [1]: %s', $limit->amount)); continue; } // limit is inside of date range if ($start->lte($limit->start_date) && $end->gte($limit->end_date)) { $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], (string) $limit->amount); - app('log')->debug(sprintf('Add full amount [2]: %s', $limit->amount)); + Log::debug(sprintf('Add full amount [2]: %s', $limit->amount)); continue; } @@ -243,7 +243,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface $days = $this->daysInOverlap($limit, $start, $end); $amount = bcmul(bcdiv((string) $limit->amount, (string) $total), (string) $days); $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $amount); - app('log')->debug( + Log::debug( sprintf( 'Amount per day: %s (%s over %d days). Total amount for %d days: %s', bcdiv((string) $limit->amount, (string) $total), @@ -283,7 +283,11 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface */ public function update(Budget $budget, array $data): Budget { - app('log')->debug('Now in update()'); + Log::debug('Now in update()'); + + // this is a lame trick to communicate with the observer. + $singleton = PreferencesSingleton::getInstance(); + $singleton->setPreference('fire_webhooks_budget_update', $data['fire_webhooks'] ?? true); $oldName = $budget->name; if (array_key_exists('name', $data)) { @@ -331,13 +335,13 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface ->where('rule_actions.action_value', $oldName) ->get(['rule_actions.*']) ; - app('log')->debug(sprintf('Found %d actions to update.', $actions->count())); + Log::debug(sprintf('Found %d actions to update.', $actions->count())); /** @var RuleAction $action */ foreach ($actions as $action) { $action->action_value = $newName; $action->save(); - app('log')->debug(sprintf('Updated action %d: %s', $action->id, $action->action_value)); + Log::debug(sprintf('Updated action %d: %s', $action->id, $action->action_value)); } } @@ -350,13 +354,13 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface ->where('rule_triggers.trigger_value', $oldName) ->get(['rule_triggers.*']) ; - app('log')->debug(sprintf('Found %d triggers to update.', $triggers->count())); + Log::debug(sprintf('Found %d triggers to update.', $triggers->count())); /** @var RuleTrigger $trigger */ foreach ($triggers as $trigger) { $trigger->trigger_value = $newName; $trigger->save(); - app('log')->debug(sprintf('Updated trigger %d: %s', $trigger->id, $trigger->trigger_value)); + Log::debug(sprintf('Updated trigger %d: %s', $trigger->id, $trigger->trigger_value)); } } @@ -487,17 +491,17 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface public function findBudget(?int $budgetId, ?string $budgetName): ?Budget { - app('log')->debug('Now in findBudget()'); - app('log')->debug(sprintf('Searching for budget with ID #%d...', $budgetId)); + Log::debug('Now in findBudget()'); + Log::debug(sprintf('Searching for budget with ID #%d...', $budgetId)); $result = $this->find((int) $budgetId); if (!$result instanceof Budget && null !== $budgetName && '' !== $budgetName) { - app('log')->debug(sprintf('Searching for budget with name %s...', $budgetName)); + Log::debug(sprintf('Searching for budget with name %s...', $budgetName)); $result = $this->findByName($budgetName); } if ($result instanceof Budget) { - app('log')->debug(sprintf('Found budget #%d: %s', $result->id, $result->name)); + Log::debug(sprintf('Found budget #%d: %s', $result->id, $result->name)); } - app('log')->debug(sprintf('Found result is null? %s', var_export(!$result instanceof Budget, true))); + Log::debug(sprintf('Found result is null? %s', var_export(!$result instanceof Budget, true))); return $result; } @@ -594,7 +598,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface public function spentInPeriod(Carbon $start, Carbon $end): array { - app('log')->debug(sprintf('Now in %s', __METHOD__)); + Log::debug(sprintf('Now in %s', __METHOD__)); $start->startOfDay(); $end->endOfDay(); @@ -656,7 +660,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface public function spentInPeriodForBudget(Budget $budget, Carbon $start, Carbon $end): array { - app('log')->debug(sprintf('Now in %s', __METHOD__)); + Log::debug(sprintf('Now in %s', __METHOD__)); $start->startOfDay(); $end->endOfDay(); @@ -740,8 +744,8 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface ] ); } catch (QueryException $e) { - app('log')->error($e->getMessage()); - app('log')->error($e->getTraceAsString()); + Log::error($e->getMessage()); + Log::error($e->getTraceAsString()); throw new FireflyException('400002: Could not store budget.', 0, $e); } From 9d9483e20f9563631ab38eba1f57a8b1feef2ad0 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 14 Sep 2025 09:00:01 +0200 Subject: [PATCH 07/91] Refactor models. --- app/Models/Account.php | 149 +++++++++++----------- app/Models/AccountMeta.php | 13 +- app/Models/AccountType.php | 72 ++++++----- app/Models/Attachment.php | 10 +- app/Models/AuditLogEntry.php | 16 +-- app/Models/AvailableBudget.php | 32 ++--- app/Models/Bill.php | 42 +++---- app/Models/Budget.php | 22 ++-- app/Models/BudgetLimit.php | 14 +-- app/Models/Category.php | 9 +- app/Models/Configuration.php | 19 ++- app/Models/CurrencyExchangeRate.php | 57 ++++----- app/Models/GroupMembership.php | 14 +-- app/Models/InvitedUser.php | 5 +- app/Models/LinkType.php | 2 +- app/Models/Location.php | 14 +-- app/Models/Note.php | 14 +-- app/Models/ObjectGroup.php | 22 ++-- app/Models/PiggyBank.php | 32 ++--- app/Models/PiggyBankEvent.php | 14 +-- app/Models/PiggyBankRepetition.php | 87 +++++++------ app/Models/Preference.php | 14 +-- app/Models/Recurrence.php | 14 +-- app/Models/RecurrenceMeta.php | 16 +-- app/Models/RecurrenceRepetition.php | 30 +++-- app/Models/RecurrenceTransaction.php | 24 ++-- app/Models/RecurrenceTransactionMeta.php | 16 +-- app/Models/Rule.php | 38 +++--- app/Models/RuleAction.php | 32 ++--- app/Models/RuleGroup.php | 21 ++-- app/Models/RuleTrigger.php | 28 ++--- app/Models/Tag.php | 9 +- app/Models/Transaction.php | 151 +++++++++++------------ app/Models/TransactionCurrency.php | 18 +-- app/Models/TransactionGroup.php | 12 +- app/Models/TransactionJournal.php | 96 +++++++------- app/Models/TransactionJournalLink.php | 55 ++++----- app/Models/TransactionJournalMeta.php | 35 +++--- app/Models/TransactionType.php | 37 +++--- app/Models/UserGroup.php | 8 +- app/Models/Webhook.php | 15 ++- app/Models/WebhookAttempt.php | 8 +- app/Models/WebhookDelivery.php | 2 +- app/Models/WebhookMessage.php | 22 ++-- app/Models/WebhookResponse.php | 2 +- app/Models/WebhookTrigger.php | 2 +- 46 files changed, 688 insertions(+), 676 deletions(-) diff --git a/app/Models/Account.php b/app/Models/Account.php index eef5d6fcd5..aedc24f00b 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -23,13 +23,13 @@ declare(strict_types=1); namespace FireflyIII\Models; -use FireflyIII\Handlers\Observer\AccountObserver; -use Illuminate\Database\Eloquent\Attributes\ObservedBy; -use Illuminate\Database\Eloquent\Attributes\Scope; use FireflyIII\Enums\AccountTypeEnum; +use FireflyIII\Handlers\Observer\AccountObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; +use Illuminate\Database\Eloquent\Attributes\Scope; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -50,9 +50,9 @@ class Account extends Model use ReturnsIntegerUserIdTrait; use SoftDeletes; - protected $fillable = ['user_id', 'user_group_id', 'account_type_id', 'name', 'active', 'virtual_balance', 'iban', 'native_virtual_balance']; + protected $fillable = ['user_id', 'user_group_id', 'account_type_id', 'name', 'active', 'virtual_balance', 'iban', 'native_virtual_balance']; - protected $hidden = ['encrypted']; + protected $hidden = ['encrypted']; private bool $joinedAccountTypes = false; /** @@ -63,13 +63,13 @@ class Account extends Model public static function routeBinder(string $value): self { if (auth()->check()) { - $accountId = (int) $value; + $accountId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|Account $account */ - $account = $user->accounts()->with(['accountType'])->find($accountId); + $account = $user->accounts()->with(['accountType'])->find($accountId); if (null !== $account) { return $account; } @@ -98,39 +98,6 @@ class Account extends Model return $this->morphMany(Attachment::class, 'attachable'); } - /** - * Get the account number. - */ - protected function accountNumber(): Attribute - { - return Attribute::make(get: function () { - /** @var null|AccountMeta $metaValue */ - $metaValue = $this->accountMeta() - ->where('name', 'account_number') - ->first() - ; - - return null !== $metaValue ? $metaValue->data : ''; - }); - } - - public function accountMeta(): HasMany - { - return $this->hasMany(AccountMeta::class); - } - - protected function editName(): Attribute - { - return Attribute::make(get: function () { - $name = $this->name; - if (AccountTypeEnum::CASH->value === $this->accountType->type) { - return ''; - } - - return $name; - }); - } - public function locations(): MorphMany { return $this->morphMany(Location::class, 'locatable'); @@ -157,19 +124,9 @@ class Account extends Model return $this->belongsToMany(PiggyBank::class); } - #[Scope] - protected function accountTypeIn(EloquentBuilder $query, array $types): void - { - if (false === $this->joinedAccountTypes) { - $query->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id'); - $this->joinedAccountTypes = true; - } - $query->whereIn('account_types.type', $types); - } - public function setVirtualBalanceAttribute(mixed $value): void { - $value = (string) $value; + $value = (string)$value; if ('' === $value) { $value = null; } @@ -189,42 +146,48 @@ class Account extends Model protected function accountId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } + /** + * Get the account number. + */ + protected function accountNumber(): Attribute + { + return Attribute::make(get: function () { + /** @var null|AccountMeta $metaValue */ + $metaValue = $this->accountMeta() + ->where('name', 'account_number') + ->first(); + + return null !== $metaValue ? $metaValue->data : ''; + }); + } + + public function accountMeta(): HasMany + { + return $this->hasMany(AccountMeta::class); + } + /** * Get the user ID */ protected function accountTypeId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } - protected function iban(): Attribute + #[Scope] + protected function accountTypeIn(EloquentBuilder $query, array $types): void { - return Attribute::make( - get: static fn ($value) => null === $value ? null : trim(str_replace(' ', '', (string) $value)), - ); - } - - protected function order(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - - /** - * Get the virtual balance - */ - protected function virtualBalance(): Attribute - { - return Attribute::make( - get: static fn ($value) => (string) $value, - ); + if (false === $this->joinedAccountTypes) { + $query->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id'); + $this->joinedAccountTypes = true; + } + $query->whereIn('account_types.type', $types); } protected function casts(): array @@ -241,4 +204,40 @@ class Account extends Model 'native_virtual_balance' => 'string', ]; } + + protected function editName(): Attribute + { + return Attribute::make(get: function () { + $name = $this->name; + if (AccountTypeEnum::CASH->value === $this->accountType->type) { + return ''; + } + + return $name; + }); + } + + protected function iban(): Attribute + { + return Attribute::make( + get: static fn($value) => null === $value ? null : trim(str_replace(' ', '', (string)$value)), + ); + } + + protected function order(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + /** + * Get the virtual balance + */ + protected function virtualBalance(): Attribute + { + return Attribute::make( + get: static fn($value) => (string)$value, + ); + } } diff --git a/app/Models/AccountMeta.php b/app/Models/AccountMeta.php index 61d8644ff3..ef87a0a508 100644 --- a/app/Models/AccountMeta.php +++ b/app/Models/AccountMeta.php @@ -23,11 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Models; -use Illuminate\Database\Eloquent\Casts\Attribute; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; - use function Safe\json_decode; use function Safe\json_encode; @@ -43,11 +42,6 @@ class AccountMeta extends Model return $this->belongsTo(Account::class); } - protected function data(): Attribute - { - return Attribute::make(get: fn (mixed $value) => (string) json_decode((string) $value, true), set: fn (mixed $value) => ['data' => json_encode($value)]); - } - protected function casts(): array { return [ @@ -55,4 +49,9 @@ class AccountMeta extends Model 'updated_at' => 'datetime', ]; } + + protected function data(): Attribute + { + return Attribute::make(get: fn(mixed $value) => (string)json_decode((string)$value, true), set: fn(mixed $value) => ['data' => json_encode($value)]); + } } diff --git a/app/Models/AccountType.php b/app/Models/AccountType.php index d4571fde77..b147593f38 100644 --- a/app/Models/AccountType.php +++ b/app/Models/AccountType.php @@ -32,55 +32,69 @@ class AccountType extends Model { use ReturnsIntegerIdTrait; - #[Deprecated] /** @deprecated */ - public const string ASSET = 'Asset account'; + #[Deprecated] + /** @deprecated */ + public const string ASSET = 'Asset account'; - #[Deprecated] /** @deprecated */ - public const string BENEFICIARY = 'Beneficiary account'; + #[Deprecated] + /** @deprecated */ + public const string BENEFICIARY = 'Beneficiary account'; - #[Deprecated] /** @deprecated */ - public const string CASH = 'Cash account'; + #[Deprecated] + /** @deprecated */ + public const string CASH = 'Cash account'; - #[Deprecated] /** @deprecated */ - public const string CREDITCARD = 'Credit card'; + #[Deprecated] + /** @deprecated */ + public const string CREDITCARD = 'Credit card'; - #[Deprecated] /** @deprecated */ - public const string DEBT = 'Debt'; + #[Deprecated] + /** @deprecated */ + public const string DEBT = 'Debt'; - #[Deprecated] /** @deprecated */ - public const string DEFAULT = 'Default account'; + #[Deprecated] + /** @deprecated */ + public const string DEFAULT = 'Default account'; - #[Deprecated] /** @deprecated */ - public const string EXPENSE = 'Expense account'; + #[Deprecated] + /** @deprecated */ + public const string EXPENSE = 'Expense account'; - #[Deprecated] /** @deprecated */ - public const string IMPORT = 'Import account'; + #[Deprecated] + /** @deprecated */ + public const string IMPORT = 'Import account'; - #[Deprecated] /** @deprecated */ - public const string INITIAL_BALANCE = 'Initial balance account'; + #[Deprecated] + /** @deprecated */ + public const string INITIAL_BALANCE = 'Initial balance account'; - #[Deprecated] /** @deprecated */ + #[Deprecated] + /** @deprecated */ public const string LIABILITY_CREDIT = 'Liability credit account'; - #[Deprecated] /** @deprecated */ - public const string LOAN = 'Loan'; + #[Deprecated] + /** @deprecated */ + public const string LOAN = 'Loan'; - #[Deprecated] /** @deprecated */ - public const string MORTGAGE = 'Mortgage'; + #[Deprecated] + /** @deprecated */ + public const string MORTGAGE = 'Mortgage'; - #[Deprecated] /** @deprecated */ - public const string RECONCILIATION = 'Reconciliation account'; + #[Deprecated] + /** @deprecated */ + public const string RECONCILIATION = 'Reconciliation account'; - #[Deprecated] /** @deprecated */ - public const string REVENUE = 'Revenue account'; + #[Deprecated] + /** @deprecated */ + public const string REVENUE = 'Revenue account'; protected $casts - = [ + = [ 'created_at' => 'datetime', 'updated_at' => 'datetime', ]; - protected $fillable = ['type']; + protected $fillable = ['type']; public function accounts(): HasMany { diff --git a/app/Models/Attachment.php b/app/Models/Attachment.php index ed5a20bab3..dd468558ab 100644 --- a/app/Models/Attachment.php +++ b/app/Models/Attachment.php @@ -53,13 +53,13 @@ class Attachment extends Model public static function routeBinder(string $value): self { if (auth()->check()) { - $attachmentId = (int) $value; + $attachmentId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|Attachment $attachment */ - $attachment = $user->attachments()->find($attachmentId); + $attachment = $user->attachments()->find($attachmentId); if (null !== $attachment) { return $attachment; } @@ -86,7 +86,7 @@ class Attachment extends Model */ public function fileName(): string { - return sprintf('at-%s.data', (string) $this->id); + return sprintf('at-%s.data', (string)$this->id); } /** @@ -100,7 +100,7 @@ class Attachment extends Model protected function attachableId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } diff --git a/app/Models/AuditLogEntry.php b/app/Models/AuditLogEntry.php index 53773692a1..baf532bfb0 100644 --- a/app/Models/AuditLogEntry.php +++ b/app/Models/AuditLogEntry.php @@ -48,14 +48,7 @@ class AuditLogEntry extends Model protected function auditableId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - - protected function changerId(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } @@ -69,4 +62,11 @@ class AuditLogEntry extends Model 'deleted_at' => 'datetime', ]; } + + protected function changerId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/AvailableBudget.php b/app/Models/AvailableBudget.php index f26f7f33d7..f3f63b4e06 100644 --- a/app/Models/AvailableBudget.php +++ b/app/Models/AvailableBudget.php @@ -84,6 +84,22 @@ class AvailableBudget extends Model ); } + protected function casts(): array + { + return [ + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'start_date' => 'date', + 'end_date' => 'date', + 'transaction_currency_id' => 'int', + 'amount' => 'string', + 'native_amount' => 'string', + 'user_id' => 'integer', + 'user_group_id' => 'integer', + ]; + } + protected function endDate(): Attribute { return Attribute::make( @@ -106,20 +122,4 @@ class AvailableBudget extends Model get: static fn($value) => (int)$value, ); } - - protected function casts(): array - { - return [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'deleted_at' => 'datetime', - 'start_date' => 'date', - 'end_date' => 'date', - 'transaction_currency_id' => 'int', - 'amount' => 'string', - 'native_amount' => 'string', - 'user_id' => 'integer', - 'user_group_id' => 'integer', - ]; - } } diff --git a/app/Models/Bill.php b/app/Models/Bill.php index e44d0f3098..a84552ba82 100644 --- a/app/Models/Bill.php +++ b/app/Models/Bill.php @@ -165,6 +165,27 @@ class Bill extends Model ); } + protected function casts(): array + { + return [ + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'date' => SeparateTimezoneCaster::class, + 'end_date' => SeparateTimezoneCaster::class, + 'extension_date' => SeparateTimezoneCaster::class, + 'skip' => 'int', + 'automatch' => 'boolean', + 'active' => 'boolean', + 'name_encrypted' => 'boolean', + 'match_encrypted' => 'boolean', + 'amount_min' => 'string', + 'amount_max' => 'string', + 'native_amount_min' => 'string', + 'native_amount_max' => 'string', + ]; + } + protected function order(): Attribute { return Attribute::make( @@ -188,25 +209,4 @@ class Bill extends Model get: static fn($value) => (int)$value, ); } - - protected function casts(): array - { - return [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'deleted_at' => 'datetime', - 'date' => SeparateTimezoneCaster::class, - 'end_date' => SeparateTimezoneCaster::class, - 'extension_date' => SeparateTimezoneCaster::class, - 'skip' => 'int', - 'automatch' => 'boolean', - 'active' => 'boolean', - 'name_encrypted' => 'boolean', - 'match_encrypted' => 'boolean', - 'amount_min' => 'string', - 'amount_max' => 'string', - 'native_amount_min' => 'string', - 'native_amount_max' => 'string', - ]; - } } diff --git a/app/Models/Budget.php b/app/Models/Budget.php index 086764d66d..1a1c2bb9dc 100644 --- a/app/Models/Budget.php +++ b/app/Models/Budget.php @@ -46,7 +46,7 @@ class Budget extends Model protected $fillable = ['user_id', 'user_group_id', 'name', 'active', 'order', 'user_group_id']; - protected $hidden = ['encrypted']; + protected $hidden = ['encrypted']; /** * Route binder. Converts the key in the URL to the specified object (or throw 404). @@ -56,13 +56,13 @@ class Budget extends Model public static function routeBinder(string $value): self { if (auth()->check()) { - $budgetId = (int) $value; + $budgetId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|Budget $budget */ - $budget = $user->budgets()->find($budgetId); + $budget = $user->budgets()->find($budgetId); if (null !== $budget) { return $budget; } @@ -109,13 +109,6 @@ class Budget extends Model return $this->belongsToMany(Transaction::class, 'budget_transaction', 'budget_id'); } - protected function order(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - protected function casts(): array { return [ @@ -128,4 +121,11 @@ class Budget extends Model 'user_group_id' => 'integer', ]; } + + protected function order(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/BudgetLimit.php b/app/Models/BudgetLimit.php index 247cf24ad4..a646d4d1e2 100644 --- a/app/Models/BudgetLimit.php +++ b/app/Models/BudgetLimit.php @@ -96,13 +96,6 @@ class BudgetLimit extends Model ); } - protected function transactionCurrencyId(): Attribute - { - return Attribute::make( - get: static fn($value) => (int)$value, - ); - } - protected function casts(): array { return [ @@ -115,4 +108,11 @@ class BudgetLimit extends Model 'native_amount' => 'string', ]; } + + protected function transactionCurrencyId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/Category.php b/app/Models/Category.php index 1f961f561d..1726c1110c 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Models; -use FireflyIII\Handlers\Observer\AccountObserver; use FireflyIII\Handlers\Observer\CategoryObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; @@ -46,7 +45,7 @@ class Category extends Model protected $fillable = ['user_id', 'user_group_id', 'name']; - protected $hidden = ['encrypted']; + protected $hidden = ['encrypted']; /** * Route binder. Converts the key in the URL to the specified object (or throw 404). @@ -56,13 +55,13 @@ class Category extends Model public static function routeBinder(string $value): self { if (auth()->check()) { - $categoryId = (int) $value; + $categoryId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|Category $category */ - $category = $user->categories()->find($categoryId); + $category = $user->categories()->find($categoryId); if (null !== $category) { return $category; } diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index 7ed2b13f2e..49e7559236 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -23,11 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Models; -use Illuminate\Database\Eloquent\Casts\Attribute; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; - use function Safe\json_decode; use function Safe\json_encode; @@ -38,14 +37,6 @@ class Configuration extends Model protected $table = 'configuration'; - /** - * TODO can be replaced with native laravel code. - */ - protected function data(): Attribute - { - return Attribute::make(get: fn ($value) => json_decode((string) $value), set: fn ($value) => ['data' => json_encode($value)]); - } - protected function casts(): array { return [ @@ -54,4 +45,12 @@ class Configuration extends Model 'deleted_at' => 'datetime', ]; } + + /** + * TODO can be replaced with native laravel code. + */ + protected function data(): Attribute + { + return Attribute::make(get: fn($value) => json_decode((string)$value), set: fn($value) => ['data' => json_encode($value)]); + } } diff --git a/app/Models/CurrencyExchangeRate.php b/app/Models/CurrencyExchangeRate.php index f5117715fe..fc919dbf80 100644 --- a/app/Models/CurrencyExchangeRate.php +++ b/app/Models/CurrencyExchangeRate.php @@ -37,6 +37,7 @@ class CurrencyExchangeRate extends Model use ReturnsIntegerIdTrait; use ReturnsIntegerUserIdTrait; use SoftDeletes; + protected $fillable = ['user_id', 'from_currency_id', 'to_currency_id', 'date', 'date_tz', 'rate']; public function fromCurrency(): BelongsTo @@ -54,34 +55,6 @@ class CurrencyExchangeRate extends Model return $this->belongsTo(User::class); } - protected function fromCurrencyId(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - - protected function rate(): Attribute - { - return Attribute::make( - get: static fn ($value) => (string) $value, - ); - } - - protected function toCurrencyId(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - - protected function userRate(): Attribute - { - return Attribute::make( - get: static fn ($value) => (string) $value, - ); - } - protected function casts(): array { return [ @@ -96,4 +69,32 @@ class CurrencyExchangeRate extends Model 'user_rate' => 'string', ]; } + + protected function fromCurrencyId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + protected function rate(): Attribute + { + return Attribute::make( + get: static fn($value) => (string)$value, + ); + } + + protected function toCurrencyId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + protected function userRate(): Attribute + { + return Attribute::make( + get: static fn($value) => (string)$value, + ); + } } diff --git a/app/Models/GroupMembership.php b/app/Models/GroupMembership.php index 830826967e..e16178fa9b 100644 --- a/app/Models/GroupMembership.php +++ b/app/Models/GroupMembership.php @@ -53,13 +53,6 @@ class GroupMembership extends Model return $this->belongsTo(UserRole::class); } - protected function userRoleId(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - protected function casts(): array { return [ @@ -69,4 +62,11 @@ class GroupMembership extends Model 'user_group_id' => 'integer', ]; } + + protected function userRoleId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/InvitedUser.php b/app/Models/InvitedUser.php index d543d82c47..272096faa8 100644 --- a/app/Models/InvitedUser.php +++ b/app/Models/InvitedUser.php @@ -36,6 +36,7 @@ class InvitedUser extends Model { use ReturnsIntegerIdTrait; use ReturnsIntegerUserIdTrait; + protected $fillable = ['user_group_id', 'user_id', 'email', 'invite_code', 'expires', 'expires_tz', 'redeemed']; /** @@ -44,10 +45,10 @@ class InvitedUser extends Model public static function routeBinder(string $value): self { if (auth()->check()) { - $attemptId = (int) $value; + $attemptId = (int)$value; /** @var null|InvitedUser $attempt */ - $attempt = self::find($attemptId); + $attempt = self::find($attemptId); if (null !== $attempt) { return $attempt; } diff --git a/app/Models/LinkType.php b/app/Models/LinkType.php index f7b662961c..e399ad2e9a 100644 --- a/app/Models/LinkType.php +++ b/app/Models/LinkType.php @@ -44,7 +44,7 @@ class LinkType extends Model public static function routeBinder(string $value): self { if (auth()->check()) { - $linkTypeId = (int) $value; + $linkTypeId = (int)$value; $linkType = self::find($linkTypeId); if (null !== $linkType) { return $linkType; diff --git a/app/Models/Location.php b/app/Models/Location.php index ce5744dba9..2e971b6f67 100644 --- a/app/Models/Location.php +++ b/app/Models/Location.php @@ -66,13 +66,6 @@ class Location extends Model return $this->morphMany(TransactionJournal::class, 'locatable'); } - protected function locatableId(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - protected function casts(): array { return [ @@ -84,4 +77,11 @@ class Location extends Model 'longitude' => 'float', ]; } + + protected function locatableId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/Note.php b/app/Models/Note.php index 6c8c56653e..71d0531976 100644 --- a/app/Models/Note.php +++ b/app/Models/Note.php @@ -44,13 +44,6 @@ class Note extends Model return $this->morphTo(); } - protected function noteableId(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - protected function casts(): array { return [ @@ -59,4 +52,11 @@ class Note extends Model 'deleted_at' => 'datetime', ]; } + + protected function noteableId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/ObjectGroup.php b/app/Models/ObjectGroup.php index d7833412fb..71a4d9b9ba 100644 --- a/app/Models/ObjectGroup.php +++ b/app/Models/ObjectGroup.php @@ -37,6 +37,7 @@ class ObjectGroup extends Model { use ReturnsIntegerIdTrait; use ReturnsIntegerUserIdTrait; + protected $fillable = ['title', 'order', 'user_id', 'user_group_id']; /** @@ -47,12 +48,11 @@ class ObjectGroup extends Model public static function routeBinder(string $value): self { if (auth()->check()) { - $objectGroupId = (int) $value; + $objectGroupId = (int)$value; /** @var null|ObjectGroup $objectGroup */ - $objectGroup = self::where('object_groups.id', $objectGroupId) - ->where('object_groups.user_id', auth()->user()->id)->first() - ; + $objectGroup = self::where('object_groups.id', $objectGroupId) + ->where('object_groups.user_id', auth()->user()->id)->first(); if (null !== $objectGroup) { return $objectGroup; } @@ -90,13 +90,6 @@ class ObjectGroup extends Model return $this->morphedByMany(PiggyBank::class, 'object_groupable'); } - protected function order(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - protected function casts(): array { return [ @@ -107,4 +100,11 @@ class ObjectGroup extends Model 'deleted_at' => 'datetime', ]; } + + protected function order(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index ae161b8c93..c493198b09 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -126,6 +126,22 @@ class PiggyBank extends Model ); } + protected function casts(): array + { + return [ + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'start_date' => 'date', + 'target_date' => 'date', + 'order' => 'int', + 'active' => 'boolean', + 'encrypted' => 'boolean', + 'target_amount' => 'string', + 'native_target_amount' => 'string', + ]; + } + protected function order(): Attribute { return Attribute::make( @@ -142,20 +158,4 @@ class PiggyBank extends Model get: static fn($value) => (string)$value, ); } - - protected function casts(): array - { - return [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'deleted_at' => 'datetime', - 'start_date' => 'date', - 'target_date' => 'date', - 'order' => 'int', - 'active' => 'boolean', - 'encrypted' => 'boolean', - 'target_amount' => 'string', - 'native_target_amount' => 'string', - ]; - } } diff --git a/app/Models/PiggyBankEvent.php b/app/Models/PiggyBankEvent.php index 9599c2f027..81578a9544 100644 --- a/app/Models/PiggyBankEvent.php +++ b/app/Models/PiggyBankEvent.php @@ -68,13 +68,6 @@ class PiggyBankEvent extends Model ); } - protected function piggyBankId(): Attribute - { - return Attribute::make( - get: static fn($value) => (int)$value, - ); - } - protected function casts(): array { return [ @@ -85,4 +78,11 @@ class PiggyBankEvent extends Model 'native_amount' => 'string', ]; } + + protected function piggyBankId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/PiggyBankRepetition.php b/app/Models/PiggyBankRepetition.php index f0f6125b53..fa6d9823ed 100644 --- a/app/Models/PiggyBankRepetition.php +++ b/app/Models/PiggyBankRepetition.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Models; -use Illuminate\Database\Eloquent\Attributes\Scope; use Carbon\Carbon; use FireflyIII\Casts\SeparateTimezoneCaster; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use Illuminate\Database\Eloquent\Attributes\Scope; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; @@ -43,12 +43,48 @@ class PiggyBankRepetition extends Model return $this->belongsTo(PiggyBank::class); } + /** + * @param mixed $value + */ + public function setCurrentAmountAttribute($value): void + { + $this->attributes['current_amount'] = (string)$value; + } + + protected function casts(): array + { + return [ + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'start_date' => SeparateTimezoneCaster::class, + 'target_date' => SeparateTimezoneCaster::class, + 'virtual_balance' => 'string', + ]; + } + + /** + * Get the amount + */ + protected function currentAmount(): Attribute + { + return Attribute::make( + get: static fn($value) => (string)$value, + ); + } + #[Scope] protected function onDates(EloquentBuilder $query, Carbon $start, Carbon $target): EloquentBuilder { return $query->where('start_date', $start->format('Y-m-d'))->where('target_date', $target->format('Y-m-d')); } + protected function piggyBankId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + /** * @return EloquentBuilder */ @@ -61,48 +97,11 @@ class PiggyBankRepetition extends Model $q->orWhereNull('start_date'); } ) - ->where( - static function (EloquentBuilder $q) use ($date): void { - $q->where('target_date', '>=', $date->format('Y-m-d 00:00:00')); - $q->orWhereNull('target_date'); - } - ) - ; - } - - /** - * @param mixed $value - */ - public function setCurrentAmountAttribute($value): void - { - $this->attributes['current_amount'] = (string) $value; - } - - /** - * Get the amount - */ - protected function currentAmount(): Attribute - { - return Attribute::make( - get: static fn ($value) => (string) $value, - ); - } - - protected function piggyBankId(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - - protected function casts(): array - { - return [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'start_date' => SeparateTimezoneCaster::class, - 'target_date' => SeparateTimezoneCaster::class, - 'virtual_balance' => 'string', - ]; + ->where( + static function (EloquentBuilder $q) use ($date): void { + $q->where('target_date', '>=', $date->format('Y-m-d 00:00:00')); + $q->orWhereNull('target_date'); + } + ); } } diff --git a/app/Models/Preference.php b/app/Models/Preference.php index 0e282cf1a5..f357f17cc7 100644 --- a/app/Models/Preference.php +++ b/app/Models/Preference.php @@ -46,16 +46,16 @@ class Preference extends Model { if (auth()->check()) { /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); // some preferences do not have an administration ID. // some need it, to make sure the correct one is selected. - $userGroupId = (int) $user->user_group_id; + $userGroupId = (int)$user->user_group_id; $userGroupId = 0 === $userGroupId ? null : $userGroupId; /** @var null|Preference $preference */ - $preference = null; - $items = config('firefly.admin_specific_prefs'); + $preference = null; + $items = config('firefly.admin_specific_prefs'); if (null !== $userGroupId && in_array($value, $items, true)) { // find a preference with a specific user_group_id $preference = $user->preferences()->where('user_group_id', $userGroupId)->where('name', $value)->first(); @@ -67,18 +67,18 @@ class Preference extends Model // try again with ID, but this time don't care about the preferred user_group_id if (null === $preference) { - $preference = $user->preferences()->where('id', (int) $value)->first(); + $preference = $user->preferences()->where('id', (int)$value)->first(); } if (null !== $preference) { /** @var Preference $preference */ return $preference; } - $default = config('firefly.default_preferences'); + $default = config('firefly.default_preferences'); if (array_key_exists($value, $default)) { $preference = new self(); $preference->name = $value; $preference->data = $default[$value]; - $preference->user_id = (int) $user->id; + $preference->user_id = (int)$user->id; $preference->user_group_id = in_array($value, $items, true) ? $userGroupId : null; $preference->save(); diff --git a/app/Models/Recurrence.php b/app/Models/Recurrence.php index 78051e6bc6..724ccdb06f 100644 --- a/app/Models/Recurrence.php +++ b/app/Models/Recurrence.php @@ -116,13 +116,6 @@ class Recurrence extends Model return $this->belongsTo(TransactionType::class); } - protected function transactionTypeId(): Attribute - { - return Attribute::make( - get: static fn($value) => (int)$value, - ); - } - protected function casts(): array { return [ @@ -142,4 +135,11 @@ class Recurrence extends Model 'user_group_id' => 'integer', ]; } + + protected function transactionTypeId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/RecurrenceMeta.php b/app/Models/RecurrenceMeta.php index 7430c942f6..fddd2e8292 100644 --- a/app/Models/RecurrenceMeta.php +++ b/app/Models/RecurrenceMeta.php @@ -37,20 +37,13 @@ class RecurrenceMeta extends Model protected $fillable = ['recurrence_id', 'name', 'value']; - protected $table = 'recurrences_meta'; + protected $table = 'recurrences_meta'; public function recurrence(): BelongsTo { return $this->belongsTo(Recurrence::class); } - protected function recurrenceId(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - protected function casts(): array { return [ @@ -61,4 +54,11 @@ class RecurrenceMeta extends Model 'value' => 'string', ]; } + + protected function recurrenceId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/RecurrenceRepetition.php b/app/Models/RecurrenceRepetition.php index 93ad02a196..920f8d93a1 100644 --- a/app/Models/RecurrenceRepetition.php +++ b/app/Models/RecurrenceRepetition.php @@ -36,20 +36,24 @@ class RecurrenceRepetition extends Model use ReturnsIntegerIdTrait; use SoftDeletes; - #[Deprecated] /** @deprecated */ - public const int WEEKEND_DO_NOTHING = 1; + #[Deprecated] + /** @deprecated */ + public const int WEEKEND_DO_NOTHING = 1; - #[Deprecated] /** @deprecated */ + #[Deprecated] + /** @deprecated */ public const int WEEKEND_SKIP_CREATION = 2; - #[Deprecated] /** @deprecated */ - public const int WEEKEND_TO_FRIDAY = 3; + #[Deprecated] + /** @deprecated */ + public const int WEEKEND_TO_FRIDAY = 3; - #[Deprecated] /** @deprecated */ - public const int WEEKEND_TO_MONDAY = 4; + #[Deprecated] + /** @deprecated */ + public const int WEEKEND_TO_MONDAY = 4; protected $casts - = [ + = [ 'created_at' => 'datetime', 'updated_at' => 'datetime', 'deleted_at' => 'datetime', @@ -59,9 +63,9 @@ class RecurrenceRepetition extends Model 'weekend' => 'int', ]; - protected $fillable = ['recurrence_id', 'weekend', 'repetition_type', 'repetition_moment', 'repetition_skip']; + protected $fillable = ['recurrence_id', 'weekend', 'repetition_type', 'repetition_moment', 'repetition_skip']; - protected $table = 'recurrences_repetitions'; + protected $table = 'recurrences_repetitions'; public function recurrence(): BelongsTo { @@ -78,21 +82,21 @@ class RecurrenceRepetition extends Model protected function recurrenceId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } protected function repetitionSkip(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } protected function weekend(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } } diff --git a/app/Models/RecurrenceTransaction.php b/app/Models/RecurrenceTransaction.php index 245e19865f..4c1a5f6f2c 100644 --- a/app/Models/RecurrenceTransaction.php +++ b/app/Models/RecurrenceTransaction.php @@ -95,6 +95,18 @@ class RecurrenceTransaction extends Model ); } + protected function casts(): array + { + return [ + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'amount' => 'string', + 'foreign_amount' => 'string', + 'description' => 'string', + ]; + } + protected function destinationId(): Attribute { return Attribute::make( @@ -136,16 +148,4 @@ class RecurrenceTransaction extends Model get: static fn($value) => (int)$value, ); } - - protected function casts(): array - { - return [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'deleted_at' => 'datetime', - 'amount' => 'string', - 'foreign_amount' => 'string', - 'description' => 'string', - ]; - } } diff --git a/app/Models/RecurrenceTransactionMeta.php b/app/Models/RecurrenceTransactionMeta.php index a334b9c433..5f2644b0f6 100644 --- a/app/Models/RecurrenceTransactionMeta.php +++ b/app/Models/RecurrenceTransactionMeta.php @@ -37,20 +37,13 @@ class RecurrenceTransactionMeta extends Model protected $fillable = ['rt_id', 'name', 'value']; - protected $table = 'rt_meta'; + protected $table = 'rt_meta'; public function recurrenceTransaction(): BelongsTo { return $this->belongsTo(RecurrenceTransaction::class, 'rt_id'); } - protected function rtId(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - protected function casts(): array { return [ @@ -61,4 +54,11 @@ class RecurrenceTransactionMeta extends Model 'value' => 'string', ]; } + + protected function rtId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/Rule.php b/app/Models/Rule.php index 78f2906ccb..a8de1ea45b 100644 --- a/app/Models/Rule.php +++ b/app/Models/Rule.php @@ -87,30 +87,11 @@ class Rule extends Model return $this->hasMany(RuleTrigger::class); } - protected function description(): Attribute - { - return Attribute::make(set: fn($value) => ['description' => e($value)]); - } - public function userGroup(): BelongsTo { return $this->belongsTo(UserGroup::class); } - protected function order(): Attribute - { - return Attribute::make( - get: static fn($value) => (int)$value, - ); - } - - protected function ruleGroupId(): Attribute - { - return Attribute::make( - get: static fn($value) => (int)$value, - ); - } - protected function casts(): array { return [ @@ -126,4 +107,23 @@ class Rule extends Model 'user_group_id' => 'integer', ]; } + + protected function description(): Attribute + { + return Attribute::make(set: fn($value) => ['description' => e($value)]); + } + + protected function order(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + protected function ruleGroupId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/RuleAction.php b/app/Models/RuleAction.php index 60ebc7254f..3603c6cc65 100644 --- a/app/Models/RuleAction.php +++ b/app/Models/RuleAction.php @@ -42,7 +42,7 @@ class RuleAction extends Model if (false === config('firefly.feature_flags.expression_engine')) { Log::debug('Expression engine is disabled, returning action value as string.'); - return (string) $this->action_value; + return (string)$this->action_value; } if (true === config('firefly.feature_flags.expression_engine') && str_starts_with($this->action_value, '\=')) { // return literal string. @@ -54,7 +54,7 @@ class RuleAction extends Model $result = $expr->evaluate($journal); } catch (SyntaxError $e) { Log::error(sprintf('Expression engine failed to evaluate expression "%s" with error "%s".', $this->action_value, $e->getMessage())); - $result = (string) $this->action_value; + $result = (string)$this->action_value; } Log::debug(sprintf('Expression engine is enabled, result of expression "%s" is "%s".', $this->action_value, $result)); @@ -66,20 +66,6 @@ class RuleAction extends Model return $this->belongsTo(Rule::class); } - protected function order(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - - protected function ruleId(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - protected function casts(): array { return [ @@ -90,4 +76,18 @@ class RuleAction extends Model 'stop_processing' => 'boolean', ]; } + + protected function order(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + protected function ruleId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/RuleGroup.php b/app/Models/RuleGroup.php index 48b96e1a67..d53155d888 100644 --- a/app/Models/RuleGroup.php +++ b/app/Models/RuleGroup.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Models; -use FireflyIII\Handlers\Observer\AccountObserver; use FireflyIII\Handlers\Observer\RuleGroupObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; @@ -53,13 +52,13 @@ class RuleGroup extends Model public static function routeBinder(string $value): self { if (auth()->check()) { - $ruleGroupId = (int) $value; + $ruleGroupId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|RuleGroup $ruleGroup */ - $ruleGroup = $user->ruleGroups()->find($ruleGroupId); + $ruleGroup = $user->ruleGroups()->find($ruleGroupId); if (null !== $ruleGroup) { return $ruleGroup; } @@ -78,13 +77,6 @@ class RuleGroup extends Model return $this->hasMany(Rule::class); } - protected function order(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - protected function casts(): array { return [ @@ -98,4 +90,11 @@ class RuleGroup extends Model 'user_group_id' => 'integer', ]; } + + protected function order(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/RuleTrigger.php b/app/Models/RuleTrigger.php index 5c9f4fca2e..9fedd62399 100644 --- a/app/Models/RuleTrigger.php +++ b/app/Models/RuleTrigger.php @@ -39,20 +39,6 @@ class RuleTrigger extends Model return $this->belongsTo(Rule::class); } - protected function order(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - - protected function ruleId(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - protected function casts(): array { return [ @@ -63,4 +49,18 @@ class RuleTrigger extends Model 'stop_processing' => 'boolean', ]; } + + protected function order(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + protected function ruleId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/Tag.php b/app/Models/Tag.php index df1082bc09..22572b8a06 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Models; use FireflyIII\Casts\SeparateTimezoneCaster; -use FireflyIII\Handlers\Observer\AccountObserver; use FireflyIII\Handlers\Observer\TagObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; @@ -46,7 +45,7 @@ class Tag extends Model protected $fillable = ['user_id', 'user_group_id', 'tag', 'date', 'date_tz', 'description', 'tag_mode']; - protected $hidden = ['zoomLevel', 'zoom_level', 'latitude', 'longitude']; + protected $hidden = ['zoomLevel', 'zoom_level', 'latitude', 'longitude']; /** * Route binder. Converts the key in the URL to the specified object (or throw 404). @@ -56,13 +55,13 @@ class Tag extends Model public static function routeBinder(string $value): self { if (auth()->check()) { - $tagId = (int) $value; + $tagId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|Tag $tag */ - $tag = $user->tags()->find($tagId); + $tag = $user->tags()->find($tagId); if (null !== $tag) { return $tag; } diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index 342a2f6b0d..e0b521d30f 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -23,12 +23,11 @@ declare(strict_types=1); namespace FireflyIII\Models; -use FireflyIII\Handlers\Observer\AccountObserver; +use Carbon\Carbon; use FireflyIII\Handlers\Observer\TransactionObserver; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Attributes\Scope; -use Carbon\Carbon; -use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -45,7 +44,7 @@ class Transaction extends Model use SoftDeletes; protected $fillable - = [ + = [ 'account_id', 'transaction_journal_id', 'description', @@ -93,6 +92,31 @@ class Transaction extends Model return $this->belongsTo(TransactionCurrency::class, 'foreign_currency_id'); } + /** + * @param mixed $value + */ + public function setAmountAttribute($value): void + { + $this->attributes['amount'] = (string)$value; + } + + public function transactionCurrency(): BelongsTo + { + return $this->belongsTo(TransactionCurrency::class); + } + + public function transactionJournal(): BelongsTo + { + return $this->belongsTo(TransactionJournal::class); + } + + protected function accountId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + /** * Check for transactions AFTER a specified date. */ @@ -121,6 +145,23 @@ class Transaction extends Model return false; } + /** + * Get the amount + */ + protected function amount(): Attribute + { + return Attribute::make( + get: static fn($value) => (string)$value, + ); + } + + protected function balanceDirty(): Attribute + { + return Attribute::make( + get: static fn($value) => 1 === (int)$value, + ); + } + /** * Check for transactions BEFORE the specified date. */ @@ -133,78 +174,6 @@ class Transaction extends Model $query->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')); } - #[Scope] - protected function transactionTypes(Builder $query, array $types): void - { - if (!self::isJoined($query, 'transaction_journals')) { - $query->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'); - } - - if (!self::isJoined($query, 'transaction_types')) { - $query->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'); - } - $query->whereIn('transaction_types.type', $types); - } - - /** - * @param mixed $value - */ - public function setAmountAttribute($value): void - { - $this->attributes['amount'] = (string) $value; - } - - public function transactionCurrency(): BelongsTo - { - return $this->belongsTo(TransactionCurrency::class); - } - - public function transactionJournal(): BelongsTo - { - return $this->belongsTo(TransactionJournal::class); - } - - protected function accountId(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - - /** - * Get the amount - */ - protected function amount(): Attribute - { - return Attribute::make( - get: static fn ($value) => (string) $value, - ); - } - - protected function balanceDirty(): Attribute - { - return Attribute::make( - get: static fn ($value) => 1 === (int) $value, - ); - } - - /** - * Get the foreign amount - */ - protected function foreignAmount(): Attribute - { - return Attribute::make( - get: static fn ($value) => (string) $value, - ); - } - - protected function transactionJournalId(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - protected function casts(): array { return [ @@ -225,4 +194,34 @@ class Transaction extends Model 'native_foreign_amount' => 'string', ]; } + + /** + * Get the foreign amount + */ + protected function foreignAmount(): Attribute + { + return Attribute::make( + get: static fn($value) => (string)$value, + ); + } + + protected function transactionJournalId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + #[Scope] + protected function transactionTypes(Builder $query, array $types): void + { + if (!self::isJoined($query, 'transaction_journals')) { + $query->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'); + } + + if (!self::isJoined($query, 'transaction_types')) { + $query->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'); + } + $query->whereIn('transaction_types.type', $types); + } } diff --git a/app/Models/TransactionCurrency.php b/app/Models/TransactionCurrency.php index 83dfd6bfe0..7ecfbd18db 100644 --- a/app/Models/TransactionCurrency.php +++ b/app/Models/TransactionCurrency.php @@ -40,7 +40,7 @@ class TransactionCurrency extends Model public ?bool $userGroupEnabled = null; public ?bool $userGroupNative = null; - protected $fillable = ['name', 'code', 'symbol', 'decimal_places', 'enabled']; + protected $fillable = ['name', 'code', 'symbol', 'decimal_places', 'enabled']; /** * Route binder. Converts the key in the URL to the specified object (or throw 404). @@ -50,7 +50,7 @@ class TransactionCurrency extends Model public static function routeBinder(string $value): self { if (auth()->check()) { - $currencyId = (int) $value; + $currencyId = (int)$value; $currency = self::find($currencyId); if (null !== $currency) { $currency->refreshForUser(auth()->user()); @@ -101,13 +101,6 @@ class TransactionCurrency extends Model return $this->belongsToMany(User::class)->withTimestamps()->withPivot('user_default'); } - protected function decimalPlaces(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - protected function casts(): array { return [ @@ -118,4 +111,11 @@ class TransactionCurrency extends Model 'enabled' => 'bool', ]; } + + protected function decimalPlaces(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/TransactionGroup.php b/app/Models/TransactionGroup.php index a13c0008ad..ec8bf0faae 100644 --- a/app/Models/TransactionGroup.php +++ b/app/Models/TransactionGroup.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Models; -use FireflyIII\Handlers\Observer\AccountObserver; use FireflyIII\Handlers\Observer\TransactionGroupObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; @@ -53,17 +52,16 @@ class TransactionGroup extends Model { app('log')->debug(sprintf('Now in %s("%s")', __METHOD__, $value)); if (auth()->check()) { - $groupId = (int) $value; + $groupId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); app('log')->debug(sprintf('User authenticated as %s', $user->email)); /** @var null|TransactionGroup $group */ - $group = $user->transactionGroups() - ->with(['transactionJournals', 'transactionJournals.transactions']) - ->where('transaction_groups.id', $groupId)->first(['transaction_groups.*']) - ; + $group = $user->transactionGroups() + ->with(['transactionJournals', 'transactionJournals.transactions']) + ->where('transaction_groups.id', $groupId)->first(['transaction_groups.*']); if (null !== $group) { app('log')->debug(sprintf('Found group #%d.', $group->id)); diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index 72f2ba70f2..dde97c2fce 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -23,16 +23,15 @@ declare(strict_types=1); namespace FireflyIII\Models; -use FireflyIII\Handlers\Observer\AccountObserver; -use FireflyIII\Handlers\Observer\TransactionJournalObserver; -use Illuminate\Database\Eloquent\Attributes\ObservedBy; -use Illuminate\Database\Eloquent\Attributes\Scope; use Carbon\Carbon; use FireflyIII\Casts\SeparateTimezoneCaster; use FireflyIII\Enums\TransactionTypeEnum; +use FireflyIII\Handlers\Observer\TransactionJournalObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; +use Illuminate\Database\Eloquent\Attributes\Scope; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -49,7 +48,6 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method EloquentBuilder|static after() * @method static EloquentBuilder|static query() */ - #[ObservedBy([TransactionJournalObserver::class])] class TransactionJournal extends Model { @@ -59,7 +57,7 @@ class TransactionJournal extends Model use SoftDeletes; protected $fillable - = [ + = [ 'user_id', 'user_group_id', 'transaction_type_id', @@ -83,13 +81,13 @@ class TransactionJournal extends Model public static function routeBinder(string $value): self { if (auth()->check()) { - $journalId = (int) $value; + $journalId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|TransactionJournal $journal */ - $journal = $user->transactionJournals()->where('transaction_journals.id', $journalId)->first(['transaction_journals.*']); + $journal = $user->transactionJournals()->where('transaction_journals.id', $journalId)->first(['transaction_journals.*']); if (null !== $journal) { return $journal; } @@ -170,32 +168,6 @@ class TransactionJournal extends Model return $query->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s')); } - #[Scope] - protected function transactionTypes(EloquentBuilder $query, array $types): void - { - if (!self::isJoined($query, 'transaction_types')) { - $query->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'); - } - if (0 !== count($types)) { - $query->whereIn('transaction_types.type', $types); - } - } - - /** - * Checks if tables are joined. - */ - public static function isJoined(EloquentBuilder $query, string $table): bool - { - $joins = $query->getQuery()->joins; - foreach ($joins as $join) { - if ($join->table === $table) { - return true; - } - } - - return false; - } - public function sourceJournalLinks(): HasMany { return $this->hasMany(TransactionJournalLink::class, 'source_id'); @@ -236,20 +208,6 @@ class TransactionJournal extends Model return $this->belongsTo(UserGroup::class); } - protected function order(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - - protected function transactionTypeId(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - protected function casts(): array { return [ @@ -268,4 +226,44 @@ class TransactionJournal extends Model 'user_group_id' => 'integer', ]; } + + protected function order(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + protected function transactionTypeId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + #[Scope] + protected function transactionTypes(EloquentBuilder $query, array $types): void + { + if (!self::isJoined($query, 'transaction_types')) { + $query->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'); + } + if (0 !== count($types)) { + $query->whereIn('transaction_types.type', $types); + } + } + + /** + * Checks if tables are joined. + */ + public static function isJoined(EloquentBuilder $query, string $table): bool + { + $joins = $query->getQuery()->joins; + foreach ($joins as $join) { + if ($join->table === $table) { + return true; + } + } + + return false; + } } diff --git a/app/Models/TransactionJournalLink.php b/app/Models/TransactionJournalLink.php index 92adb6a7e2..4880d15c39 100644 --- a/app/Models/TransactionJournalLink.php +++ b/app/Models/TransactionJournalLink.php @@ -44,14 +44,13 @@ class TransactionJournalLink extends Model public static function routeBinder(string $value): self { if (auth()->check()) { - $linkId = (int) $value; + $linkId = (int)$value; $link = self::where('journal_links.id', $linkId) - ->leftJoin('transaction_journals as t_a', 't_a.id', '=', 'source_id') - ->leftJoin('transaction_journals as t_b', 't_b.id', '=', 'destination_id') - ->where('t_a.user_id', auth()->user()->id) - ->where('t_b.user_id', auth()->user()->id) - ->first(['journal_links.*']) - ; + ->leftJoin('transaction_journals as t_a', 't_a.id', '=', 'source_id') + ->leftJoin('transaction_journals as t_b', 't_b.id', '=', 'destination_id') + ->where('t_a.user_id', auth()->user()->id) + ->where('t_b.user_id', auth()->user()->id) + ->first(['journal_links.*']); if (null !== $link) { return $link; } @@ -83,27 +82,6 @@ class TransactionJournalLink extends Model return $this->belongsTo(TransactionJournal::class, 'source_id'); } - protected function destinationId(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - - protected function linkTypeId(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - - protected function sourceId(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - protected function casts(): array { return [ @@ -111,4 +89,25 @@ class TransactionJournalLink extends Model 'updated_at' => 'datetime', ]; } + + protected function destinationId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + protected function linkTypeId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + protected function sourceId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/TransactionJournalMeta.php b/app/Models/TransactionJournalMeta.php index 80b7e42ca0..7dd67b6f86 100644 --- a/app/Models/TransactionJournalMeta.php +++ b/app/Models/TransactionJournalMeta.php @@ -28,7 +28,6 @@ use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; - use function Safe\json_decode; use function Safe\json_encode; @@ -39,29 +38,13 @@ class TransactionJournalMeta extends Model protected $fillable = ['transaction_journal_id', 'name', 'data', 'hash']; - protected $table = 'journal_meta'; - - protected function data(): Attribute - { - return Attribute::make(get: fn ($value) => json_decode((string) $value, false), set: function ($value) { - $data = json_encode($value); - - return ['data' => $data, 'hash' => hash('sha256', $data)]; - }); - } + protected $table = 'journal_meta'; public function transactionJournal(): BelongsTo { return $this->belongsTo(TransactionJournal::class); } - protected function transactionJournalId(): Attribute - { - return Attribute::make( - get: static fn ($value) => (int) $value, - ); - } - protected function casts(): array { return [ @@ -70,4 +53,20 @@ class TransactionJournalMeta extends Model 'deleted_at' => 'datetime', ]; } + + protected function data(): Attribute + { + return Attribute::make(get: fn($value) => json_decode((string)$value, false), set: function ($value) { + $data = json_encode($value); + + return ['data' => $data, 'hash' => hash('sha256', $data)]; + }); + } + + protected function transactionJournalId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/TransactionType.php b/app/Models/TransactionType.php index 0b04ead8b5..4062911fae 100644 --- a/app/Models/TransactionType.php +++ b/app/Models/TransactionType.php @@ -36,34 +36,41 @@ class TransactionType extends Model use ReturnsIntegerIdTrait; use SoftDeletes; - #[Deprecated] /** @deprecated */ - public const string DEPOSIT = 'Deposit'; + #[Deprecated] + /** @deprecated */ + public const string DEPOSIT = 'Deposit'; - #[Deprecated] /** @deprecated */ - public const string INVALID = 'Invalid'; + #[Deprecated] + /** @deprecated */ + public const string INVALID = 'Invalid'; - #[Deprecated] /** @deprecated */ + #[Deprecated] + /** @deprecated */ public const string LIABILITY_CREDIT = 'Liability credit'; - #[Deprecated] /** @deprecated */ - public const string OPENING_BALANCE = 'Opening balance'; + #[Deprecated] + /** @deprecated */ + public const string OPENING_BALANCE = 'Opening balance'; - #[Deprecated] /** @deprecated */ - public const string RECONCILIATION = 'Reconciliation'; + #[Deprecated] + /** @deprecated */ + public const string RECONCILIATION = 'Reconciliation'; - #[Deprecated] /** @deprecated */ - public const string TRANSFER = 'Transfer'; + #[Deprecated] + /** @deprecated */ + public const string TRANSFER = 'Transfer'; - #[Deprecated] /** @deprecated */ - public const string WITHDRAWAL = 'Withdrawal'; + #[Deprecated] + /** @deprecated */ + public const string WITHDRAWAL = 'Withdrawal'; protected $casts - = [ + = [ 'created_at' => 'datetime', 'updated_at' => 'datetime', 'deleted_at' => 'datetime', ]; - protected $fillable = ['type']; + protected $fillable = ['type']; /** * Route binder. Converts the key in the URL to the specified object (or throw 404). diff --git a/app/Models/UserGroup.php b/app/Models/UserGroup.php index fbae2d70f6..7b20a2980e 100644 --- a/app/Models/UserGroup.php +++ b/app/Models/UserGroup.php @@ -47,19 +47,19 @@ class UserGroup extends Model public static function routeBinder(string $value): self { if (auth()->check()) { - $userGroupId = (int) $value; + $userGroupId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|UserGroup $userGroup */ - $userGroup = self::find($userGroupId); + $userGroup = self::find($userGroupId); if (null === $userGroup) { throw new NotFoundHttpException(); } // need at least ready only to be aware of the user group's existence, // but owner/full role (in the group) or global owner role may overrule this. - $access = $user->hasRoleInGroupOrOwner($userGroup, UserRoleEnum::READ_ONLY) || $user->hasRole('owner'); + $access = $user->hasRoleInGroupOrOwner($userGroup, UserRoleEnum::READ_ONLY) || $user->hasRole('owner'); if ($access) { return $userGroup; } diff --git a/app/Models/Webhook.php b/app/Models/Webhook.php index 797ca82876..5b19fe269c 100644 --- a/app/Models/Webhook.php +++ b/app/Models/Webhook.php @@ -27,7 +27,6 @@ namespace FireflyIII\Models; use FireflyIII\Enums\WebhookDelivery as WebhookDeliveryEnum; use FireflyIII\Enums\WebhookResponse as WebhookResponseEnum; use FireflyIII\Enums\WebhookTrigger as WebhookTriggerEnum; -use FireflyIII\Handlers\Observer\AccountObserver; use FireflyIII\Handlers\Observer\WebhookObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; @@ -138,10 +137,10 @@ class Webhook extends Model $webhookId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|Webhook $webhook */ - $webhook = $user->webhooks()->find($webhookId); + $webhook = $user->webhooks()->find($webhookId); if (null !== $webhook) { return $webhook; } @@ -155,16 +154,16 @@ class Webhook extends Model return $this->belongsTo(User::class); } - public function webhookMessages(): HasMany - { - return $this->hasMany(WebhookMessage::class); - } - public function webhookDeliveries(): BelongsToMany { return $this->belongsToMany(WebhookDelivery::class); } + public function webhookMessages(): HasMany + { + return $this->hasMany(WebhookMessage::class); + } + public function webhookResponses(): BelongsToMany { return $this->belongsToMany(WebhookResponse::class); diff --git a/app/Models/WebhookAttempt.php b/app/Models/WebhookAttempt.php index fa283f9ca2..c38fd15fe1 100644 --- a/app/Models/WebhookAttempt.php +++ b/app/Models/WebhookAttempt.php @@ -45,13 +45,13 @@ class WebhookAttempt extends Model public static function routeBinder(string $value): self { if (auth()->check()) { - $attemptId = (int) $value; + $attemptId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|WebhookAttempt $attempt */ - $attempt = self::find($attemptId); + $attempt = self::find($attemptId); if (null !== $attempt && $attempt->webhookMessage->webhook->user_id === $user->id) { return $attempt; } @@ -68,7 +68,7 @@ class WebhookAttempt extends Model protected function webhookMessageId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } } diff --git a/app/Models/WebhookDelivery.php b/app/Models/WebhookDelivery.php index a43a47d417..614a5cb3c2 100644 --- a/app/Models/WebhookDelivery.php +++ b/app/Models/WebhookDelivery.php @@ -41,7 +41,7 @@ class WebhookDelivery extends Model protected function key(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } } diff --git a/app/Models/WebhookMessage.php b/app/Models/WebhookMessage.php index c1014d60aa..cae9a4a73f 100644 --- a/app/Models/WebhookMessage.php +++ b/app/Models/WebhookMessage.php @@ -72,6 +72,17 @@ class WebhookMessage extends Model return $this->hasMany(WebhookAttempt::class); } + protected function casts(): array + { + return [ + 'sent' => 'boolean', + 'errored' => 'boolean', + 'uuid' => 'string', + 'message' => 'json', + 'logs' => 'json', + ]; + } + /** * Get the amount */ @@ -88,15 +99,4 @@ class WebhookMessage extends Model get: static fn($value) => (int)$value, ); } - - protected function casts(): array - { - return [ - 'sent' => 'boolean', - 'errored' => 'boolean', - 'uuid' => 'string', - 'message' => 'json', - 'logs' => 'json', - ]; - } } diff --git a/app/Models/WebhookResponse.php b/app/Models/WebhookResponse.php index c970e8b70e..5c8cb45311 100644 --- a/app/Models/WebhookResponse.php +++ b/app/Models/WebhookResponse.php @@ -41,7 +41,7 @@ class WebhookResponse extends Model protected function key(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } } diff --git a/app/Models/WebhookTrigger.php b/app/Models/WebhookTrigger.php index 4bd8cf444d..7d9a6ea1fa 100644 --- a/app/Models/WebhookTrigger.php +++ b/app/Models/WebhookTrigger.php @@ -41,7 +41,7 @@ class WebhookTrigger extends Model protected function key(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } } From 768bd892c8a9a533bcb0327da77ada62fb1423fa Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 14 Sep 2025 09:14:41 +0200 Subject: [PATCH 08/91] Allow sending of webhooks from budget limit store. --- .../Models/BudgetLimit/StoreController.php | 23 ++++---- .../Models/BudgetLimit/StoreRequest.php | 58 +++++++++++++++++++ app/Handlers/Observer/BudgetLimitObserver.php | 26 ++++++--- .../Budget/BudgetLimitRepository.php | 24 +++++--- resources/lang/en_US/validation.php | 1 + 5 files changed, 103 insertions(+), 29 deletions(-) diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/StoreController.php b/app/Api/V1/Controllers/Models/BudgetLimit/StoreController.php index c3b88d69f2..910502662c 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/StoreController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/StoreController.php @@ -67,26 +67,27 @@ class StoreController extends Controller */ public function store(StoreRequest $request, Budget $budget): JsonResponse { - $data = $request->getAll(); - $data['start_date'] = $data['start']; - $data['end_date'] = $data['end']; - $data['budget_id'] = $budget->id; + $data = $request->getAll(); + $data['start_date'] = $data['start']; + $data['end_date'] = $data['end']; + $data['fire_webhooks'] = $data['fire_webhooks'] ?? true; + $data['budget_id'] = $budget->id; - $budgetLimit = $this->blRepository->store($data); - $manager = $this->getManager(); + $budgetLimit = $this->blRepository->store($data); + $manager = $this->getManager(); // enrich /** @var User $admin */ - $admin = auth()->user(); - $enrichment = new BudgetLimitEnrichment(); + $admin = auth()->user(); + $enrichment = new BudgetLimitEnrichment(); $enrichment->setUser($admin); - $budgetLimit = $enrichment->enrichSingle($budgetLimit); + $budgetLimit = $enrichment->enrichSingle($budgetLimit); /** @var BudgetLimitTransformer $transformer */ - $transformer = app(BudgetLimitTransformer::class); + $transformer = app(BudgetLimitTransformer::class); $transformer->setParameters($this->parameters); - $resource = new Item($budgetLimit, $transformer, 'budget_limits'); + $resource = new Item($budgetLimit, $transformer, 'budget_limits'); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } diff --git a/app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php b/app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php index 48c77cf2a2..2afabc38f3 100644 --- a/app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php +++ b/app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php @@ -24,10 +24,18 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\BudgetLimit; +use Carbon\Carbon; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Factory\TransactionCurrencyFactory; +use FireflyIII\Models\Budget; +use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsValidPositiveAmount; +use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class StoreRequest @@ -49,6 +57,9 @@ class StoreRequest extends FormRequest 'currency_id' => $this->convertInteger('currency_id'), 'currency_code' => $this->convertString('currency_code'), 'notes' => $this->stringWithNewlines('notes'), + + // for webhooks: + 'fire_webhooks' => $this->boolean('fire_webhooks', true), ]; } @@ -64,6 +75,53 @@ class StoreRequest extends FormRequest 'currency_id' => 'numeric|exists:transaction_currencies,id', 'currency_code' => 'min:3|max:51|exists:transaction_currencies,code', 'notes' => 'nullable|min:0|max:32768', + + // webhooks + 'fire_webhooks' => [new IsBoolean()], ]; } + + /** + * Configure the validator instance. + */ + public function withValidator(Validator $validator): void + { + $budget = $this->route()->parameter('budget'); + $validator->after( + static function (Validator $validator) use ($budget): void { + if(0 !== count($validator->failed())) { + return; + } + $data = $validator->getData(); + + // if no currency has been provided, use the user's default currency: + /** @var TransactionCurrencyFactory $factory */ + $factory = app(TransactionCurrencyFactory::class); + $currency = $factory->find($data['currency_id'] ?? null, $data['currency_code'] ?? null); + if (null === $currency) { + $currency = Amount::getPrimaryCurrency(); + } + $currency->enabled = true; + $currency->save(); + + // validator already concluded start and end are valid dates: + $start = Carbon::parse($data['start'], config('app.timezone')); + $end = Carbon::parse($data['end'], config('app.timezone')); + + // find limit with same date range and currency. + $limit = $budget->budgetlimits() + ->where('budget_limits.start_date', $start->format('Y-m-d')) + ->where('budget_limits.end_date', $end->format('Y-m-d')) + ->where('budget_limits.transaction_currency_id', $currency->id) + ->first(['budget_limits.*']) + ; + if(null !== $limit) { + $validator->errors()->add('start', trans('validation.limit_exists')); + } + } + ); + if ($validator->fails()) { + Log::channel('audit')->error(sprintf('Validation errors in %s', self::class), $validator->errors()->toArray()); + } + } } diff --git a/app/Handlers/Observer/BudgetLimitObserver.php b/app/Handlers/Observer/BudgetLimitObserver.php index ae6b1aac6a..c29b30dbde 100644 --- a/app/Handlers/Observer/BudgetLimitObserver.php +++ b/app/Handlers/Observer/BudgetLimitObserver.php @@ -31,6 +31,7 @@ use FireflyIII\Models\BudgetLimit; use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Http\Api\ExchangeRateConverter; use FireflyIII\Support\Observers\RecalculatesAvailableBudgetsTrait; +use FireflyIII\Support\Singleton\PreferencesSingleton; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; @@ -44,17 +45,24 @@ class BudgetLimitObserver $this->updatePrimaryCurrencyAmount($budgetLimit); $this->updateAvailableBudget($budgetLimit); - $user = $budgetLimit->budget->user; - /** @var MessageGeneratorInterface $engine */ - $engine = app(MessageGeneratorInterface::class); - $engine->setUser($user); - $engine->setObjects(new Collection()->push($budgetLimit)); - $engine->setTrigger(WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT); - $engine->generateMessages(); + // this is a lame trick to communicate with the observer. + $singleton = PreferencesSingleton::getInstance(); - Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__)); - event(new RequestedSendWebhookMessages()); + if (true === $singleton->getPreference('fire_webhooks_bl_store')) { + + $user = $budgetLimit->budget->user; + + /** @var MessageGeneratorInterface $engine */ + $engine = app(MessageGeneratorInterface::class); + $engine->setUser($user); + $engine->setObjects(new Collection()->push($budgetLimit)); + $engine->setTrigger(WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT); + $engine->generateMessages(); + + Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__)); + event(new RequestedSendWebhookMessages()); + } } private function updatePrimaryCurrencyAmount(BudgetLimit $budgetLimit): void diff --git a/app/Repositories/Budget/BudgetLimitRepository.php b/app/Repositories/Budget/BudgetLimitRepository.php index 8974613364..67378a92d4 100644 --- a/app/Repositories/Budget/BudgetLimitRepository.php +++ b/app/Repositories/Budget/BudgetLimitRepository.php @@ -31,8 +31,10 @@ use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\Note; use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; +use FireflyIII\Support\Singleton\PreferencesSingleton; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; @@ -271,7 +273,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface, UserGroup $factory = app(TransactionCurrencyFactory::class); $currency = $factory->find($data['currency_id'] ?? null, $data['currency_code'] ?? null); if (null === $currency) { - $currency = app('amount')->getPrimaryCurrencyByUserGroup($this->user->userGroup); + $currency = Amount::getPrimaryCurrencyByUserGroup($this->user->userGroup); } $currency->enabled = true; $currency->save(); @@ -293,7 +295,11 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface, UserGroup if (null !== $limit) { throw new FireflyException('200027: Budget limit already exists.'); } - app('log')->debug('No existing budget limit, create a new one'); + Log::debug('No existing budget limit, create a new one'); + + // this is a lame trick to communicate with the observer. + $singleton = PreferencesSingleton::getInstance(); + $singleton->setPreference('fire_webhooks_bl_store', $data['fire_webhooks'] ?? true); // or create one and return it. $limit = new BudgetLimit(); @@ -309,7 +315,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface, UserGroup $this->setNoteText($limit, $noteText); } - app('log')->debug(sprintf('Created new budget limit with ID #%d and amount %s', $limit->id, $data['amount'])); + Log::debug(sprintf('Created new budget limit with ID #%d and amount %s', $limit->id, $data['amount'])); return $limit; } @@ -393,7 +399,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface, UserGroup ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) ->count('budget_limits.*') ; - app('log')->debug(sprintf('Found %d budget limits.', $limits)); + Log::debug(sprintf('Found %d budget limits.', $limits)); // there might be a budget limit for these dates: /** @var null|BudgetLimit $limit */ @@ -405,7 +411,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface, UserGroup // if more than 1 limit found, delete the others: if ($limits > 1 && null !== $limit) { - app('log')->debug(sprintf('Found more than 1, delete all except #%d', $limit->id)); + Log::debug(sprintf('Found more than 1, delete all except #%d', $limit->id)); $budget->budgetlimits() ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00')) ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) @@ -417,20 +423,20 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface, UserGroup // Returns 0 if the two operands are equal, // 1 if the left_operand is larger than the right_operand, -1 otherwise. if (null !== $limit && bccomp($amount, '0') <= 0) { - app('log')->debug(sprintf('%s is zero, delete budget limit #%d', $amount, $limit->id)); + Log::debug(sprintf('%s is zero, delete budget limit #%d', $amount, $limit->id)); $limit->delete(); return null; } // update if exists: if (null !== $limit) { - app('log')->debug(sprintf('Existing budget limit is #%d, update this to amount %s', $limit->id, $amount)); + Log::debug(sprintf('Existing budget limit is #%d, update this to amount %s', $limit->id, $amount)); $limit->amount = $amount; $limit->save(); return $limit; } - app('log')->debug('No existing budget limit, create a new one'); + Log::debug('No existing budget limit, create a new one'); // or create one and return it. $limit = new BudgetLimit(); $limit->budget()->associate($budget); @@ -440,7 +446,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface, UserGroup $limit->end_date_tz = $end->format('e'); $limit->amount = $amount; $limit->save(); - app('log')->debug(sprintf('Created new budget limit with ID #%d and amount %s', $limit->id, $amount)); + Log::debug(sprintf('Created new budget limit with ID #%d and amount %s', $limit->id, $amount)); return $limit; } diff --git a/resources/lang/en_US/validation.php b/resources/lang/en_US/validation.php index 9c9052b050..7c2134f5a2 100644 --- a/resources/lang/en_US/validation.php +++ b/resources/lang/en_US/validation.php @@ -24,6 +24,7 @@ declare(strict_types=1); return [ + 'limit_exists' => 'There is already a budget limit (amount) for this budget and currency in the given period.', 'invalid_sort_instruction' => 'The sort instruction is invalid for an object of type ":object".', 'invalid_sort_instruction_index' => 'The sort instruction at index #:index is invalid for an object of type ":object".', 'no_sort_instructions' => 'There are no sort instructions defined for an object of type ":object".', From 9075fa8ac8690763ce5fabce362567986c47d235 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 14 Sep 2025 09:21:32 +0200 Subject: [PATCH 09/91] Allow webhooks to be send for budget limit update. --- .../Models/BudgetLimit/UpdateController.php | 1 + .../Models/BudgetLimit/UpdateRequest.php | 7 + app/Handlers/Observer/BudgetLimitObserver.php | 25 ++-- .../Budget/BudgetLimitRepository.php | 124 +++++++++--------- .../Budget/BudgetLimitRepositoryInterface.php | 2 +- 5 files changed, 88 insertions(+), 71 deletions(-) diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php b/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php index cd17067b5c..cca3953140 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php @@ -77,6 +77,7 @@ class UpdateController extends Controller throw new FireflyException('20028: The budget limit does not belong to the budget.'); } $data = $request->getAll(); + $data['fire_webhooks'] = $data['fire_webhooks'] ?? true; $data['budget_id'] = $budget->id; $budgetLimit = $this->blRepository->update($budgetLimit, $data); $manager = $this->getManager(); diff --git a/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php b/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php index 42f7849292..4f877b4865 100644 --- a/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\BudgetLimit; +use FireflyIII\Rules\IsBoolean; use Illuminate\Validation\Validator; use Carbon\Carbon; use FireflyIII\Rules\IsValidPositiveAmount; @@ -52,6 +53,9 @@ class UpdateRequest extends FormRequest 'currency_id' => ['currency_id', 'convertInteger'], 'currency_code' => ['currency_code', 'convertString'], 'notes' => ['notes', 'stringWithNewlines'], + + // webhooks + 'fire_webhooks' => ['fire_webhooks','boolean'] ]; if (false === $this->has('notes')) { // ignore notes, not submitted. @@ -73,6 +77,9 @@ class UpdateRequest extends FormRequest 'currency_id' => 'numeric|exists:transaction_currencies,id', 'currency_code' => 'min:3|max:51|exists:transaction_currencies,code', 'notes' => 'nullable|min:0|max:32768', + + // webhooks + 'fire_webhooks' => [new IsBoolean()], ]; } diff --git a/app/Handlers/Observer/BudgetLimitObserver.php b/app/Handlers/Observer/BudgetLimitObserver.php index c29b30dbde..4fb16a06fa 100644 --- a/app/Handlers/Observer/BudgetLimitObserver.php +++ b/app/Handlers/Observer/BudgetLimitObserver.php @@ -75,7 +75,7 @@ class BudgetLimitObserver $userCurrency = app('amount')->getPrimaryCurrencyByUserGroup($budgetLimit->budget->user->userGroup); $budgetLimit->native_amount = null; if ($budgetLimit->transactionCurrency->id !== $userCurrency->id) { - $converter = new ExchangeRateConverter(); + $converter = new ExchangeRateConverter(); $converter->setUserGroup($budgetLimit->budget->user->userGroup); $converter->setIgnoreSettings(true); $budgetLimit->native_amount = $converter->convert($budgetLimit->transactionCurrency, $userCurrency, today(), $budgetLimit->amount); @@ -90,16 +90,21 @@ class BudgetLimitObserver $this->updatePrimaryCurrencyAmount($budgetLimit); $this->updateAvailableBudget($budgetLimit); - $user = $budgetLimit->budget->user; + // this is a lame trick to communicate with the observer. + $singleton = PreferencesSingleton::getInstance(); - /** @var MessageGeneratorInterface $engine */ - $engine = app(MessageGeneratorInterface::class); - $engine->setUser($user); - $engine->setObjects(new Collection()->push($budgetLimit)); - $engine->setTrigger(WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT); - $engine->generateMessages(); + if (true === $singleton->getPreference('fire_webhooks_bl_update')) { + $user = $budgetLimit->budget->user; - Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__)); - event(new RequestedSendWebhookMessages()); + /** @var MessageGeneratorInterface $engine */ + $engine = app(MessageGeneratorInterface::class); + $engine->setUser($user); + $engine->setObjects(new Collection()->push($budgetLimit)); + $engine->setTrigger(WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT); + $engine->generateMessages(); + + Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__)); + event(new RequestedSendWebhookMessages()); + } } } diff --git a/app/Repositories/Budget/BudgetLimitRepository.php b/app/Repositories/Budget/BudgetLimitRepository.php index 67378a92d4..857052b081 100644 --- a/app/Repositories/Budget/BudgetLimitRepository.php +++ b/app/Repositories/Budget/BudgetLimitRepository.php @@ -375,11 +375,15 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface, UserGroup } // catch unexpected null: if (null === $currency) { - $currency = $budgetLimit->transactionCurrency ?? app('amount')->getPrimaryCurrencyByUserGroup($this->user->userGroup); + $currency = $budgetLimit->transactionCurrency ?? Amount::getPrimaryCurrencyByUserGroup($this->user->userGroup); } $currency->enabled = true; $currency->save(); + // this is a lame trick to communicate with the observer. + $singleton = PreferencesSingleton::getInstance(); + $singleton->setPreference('fire_webhooks_bl_update', $data['fire_webhooks'] ?? true); + $budgetLimit->transaction_currency_id = $currency->id; $budgetLimit->save(); @@ -391,63 +395,63 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface, UserGroup return $budgetLimit; } - public function updateLimitAmount(Budget $budget, Carbon $start, Carbon $end, string $amount): ?BudgetLimit - { - // count the limits: - $limits = $budget->budgetlimits() - ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00')) - ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) - ->count('budget_limits.*') - ; - Log::debug(sprintf('Found %d budget limits.', $limits)); - - // there might be a budget limit for these dates: - /** @var null|BudgetLimit $limit */ - $limit = $budget->budgetlimits() - ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00')) - ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) - ->first(['budget_limits.*']) - ; - - // if more than 1 limit found, delete the others: - if ($limits > 1 && null !== $limit) { - Log::debug(sprintf('Found more than 1, delete all except #%d', $limit->id)); - $budget->budgetlimits() - ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00')) - ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) - ->where('budget_limits.id', '!=', $limit->id)->delete() - ; - } - - // delete if amount is zero. - // Returns 0 if the two operands are equal, - // 1 if the left_operand is larger than the right_operand, -1 otherwise. - if (null !== $limit && bccomp($amount, '0') <= 0) { - Log::debug(sprintf('%s is zero, delete budget limit #%d', $amount, $limit->id)); - $limit->delete(); - - return null; - } - // update if exists: - if (null !== $limit) { - Log::debug(sprintf('Existing budget limit is #%d, update this to amount %s', $limit->id, $amount)); - $limit->amount = $amount; - $limit->save(); - - return $limit; - } - Log::debug('No existing budget limit, create a new one'); - // or create one and return it. - $limit = new BudgetLimit(); - $limit->budget()->associate($budget); - $limit->start_date = $start->startOfDay(); - $limit->start_date_tz = $start->format('e'); - $limit->end_date = $end->startOfDay(); - $limit->end_date_tz = $end->format('e'); - $limit->amount = $amount; - $limit->save(); - Log::debug(sprintf('Created new budget limit with ID #%d and amount %s', $limit->id, $amount)); - - return $limit; - } +// public function updateLimitAmount(Budget $budget, Carbon $start, Carbon $end, string $amount): ?BudgetLimit +// { +// // count the limits: +// $limits = $budget->budgetlimits() +// ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00')) +// ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) +// ->count('budget_limits.*') +// ; +// Log::debug(sprintf('Found %d budget limits.', $limits)); +// +// // there might be a budget limit for these dates: +// /** @var null|BudgetLimit $limit */ +// $limit = $budget->budgetlimits() +// ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00')) +// ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) +// ->first(['budget_limits.*']) +// ; +// +// // if more than 1 limit found, delete the others: +// if ($limits > 1 && null !== $limit) { +// Log::debug(sprintf('Found more than 1, delete all except #%d', $limit->id)); +// $budget->budgetlimits() +// ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00')) +// ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) +// ->where('budget_limits.id', '!=', $limit->id)->delete() +// ; +// } +// +// // delete if amount is zero. +// // Returns 0 if the two operands are equal, +// // 1 if the left_operand is larger than the right_operand, -1 otherwise. +// if (null !== $limit && bccomp($amount, '0') <= 0) { +// Log::debug(sprintf('%s is zero, delete budget limit #%d', $amount, $limit->id)); +// $limit->delete(); +// +// return null; +// } +// // update if exists: +// if (null !== $limit) { +// Log::debug(sprintf('Existing budget limit is #%d, update this to amount %s', $limit->id, $amount)); +// $limit->amount = $amount; +// $limit->save(); +// +// return $limit; +// } +// Log::debug('No existing budget limit, create a new one'); +// // or create one and return it. +// $limit = new BudgetLimit(); +// $limit->budget()->associate($budget); +// $limit->start_date = $start->startOfDay(); +// $limit->start_date_tz = $start->format('e'); +// $limit->end_date = $end->startOfDay(); +// $limit->end_date_tz = $end->format('e'); +// $limit->amount = $amount; +// $limit->save(); +// Log::debug(sprintf('Created new budget limit with ID #%d and amount %s', $limit->id, $amount)); +// +// return $limit; +// } } diff --git a/app/Repositories/Budget/BudgetLimitRepositoryInterface.php b/app/Repositories/Budget/BudgetLimitRepositoryInterface.php index c56093ef61..7d8ccde5bb 100644 --- a/app/Repositories/Budget/BudgetLimitRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetLimitRepositoryInterface.php @@ -81,5 +81,5 @@ interface BudgetLimitRepositoryInterface public function update(BudgetLimit $budgetLimit, array $data): BudgetLimit; - public function updateLimitAmount(Budget $budget, Carbon $start, Carbon $end, string $amount): ?BudgetLimit; + //public function updateLimitAmount(Budget $budget, Carbon $start, Carbon $end, string $amount): ?BudgetLimit; } From de9efb0727f394bb26f29ae9efae003c63042882 Mon Sep 17 00:00:00 2001 From: JC5 Date: Mon, 15 Sep 2025 05:23:47 +0200 Subject: [PATCH 10/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-09-15?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Models/Budget/StoreController.php | 16 +-- .../Models/Budget/UpdateController.php | 16 +-- .../Models/BudgetLimit/StoreController.php | 24 ++-- .../Models/BudgetLimit/UpdateController.php | 2 +- .../Requests/Models/Budget/StoreRequest.php | 36 +++--- .../Requests/Models/Budget/UpdateRequest.php | 36 +++--- .../Models/BudgetLimit/StoreRequest.php | 42 +++--- .../Models/BudgetLimit/UpdateRequest.php | 26 ++-- .../Models/Transaction/StoreRequest.php | 2 +- app/Handlers/Observer/BudgetLimitObserver.php | 6 +- app/Handlers/Observer/BudgetObserver.php | 10 +- app/Models/Account.php | 25 ++-- app/Models/AccountMeta.php | 3 +- app/Models/AccountType.php | 30 ++--- app/Models/Attachment.php | 6 +- app/Models/AuditLogEntry.php | 4 +- app/Models/AutoBudget.php | 12 +- app/Models/AvailableBudget.php | 16 +-- app/Models/Bill.php | 16 +-- app/Models/Budget.php | 8 +- app/Models/BudgetLimit.php | 13 +- app/Models/Category.php | 6 +- app/Models/Configuration.php | 3 +- app/Models/CurrencyExchangeRate.php | 8 +- app/Models/GroupMembership.php | 2 +- app/Models/InvitedUser.php | 2 +- app/Models/Location.php | 2 +- app/Models/Note.php | 2 +- app/Models/ObjectGroup.php | 7 +- app/Models/PiggyBank.php | 13 +- app/Models/PiggyBankEvent.php | 6 +- app/Models/PiggyBankRepetition.php | 17 +-- app/Models/Preference.php | 8 +- app/Models/Recurrence.php | 8 +- app/Models/RecurrenceMeta.php | 4 +- app/Models/RecurrenceRepetition.php | 18 +-- app/Models/RecurrenceTransaction.php | 16 +-- app/Models/RecurrenceTransactionMeta.php | 4 +- app/Models/Rule.php | 10 +- app/Models/RuleAction.php | 4 +- app/Models/RuleGroup.php | 6 +- app/Models/RuleTrigger.php | 4 +- app/Models/Tag.php | 6 +- app/Models/Transaction.php | 12 +- app/Models/TransactionCurrency.php | 4 +- app/Models/TransactionGroup.php | 9 +- app/Models/TransactionJournal.php | 10 +- app/Models/TransactionJournalLink.php | 17 +-- app/Models/TransactionJournalMeta.php | 7 +- app/Models/TransactionType.php | 16 +-- app/Models/UserGroup.php | 6 +- app/Models/Webhook.php | 4 +- app/Models/WebhookAttempt.php | 6 +- app/Models/WebhookDelivery.php | 2 +- app/Models/WebhookMessage.php | 8 +- app/Models/WebhookResponse.php | 2 +- app/Models/WebhookTrigger.php | 2 +- app/Providers/EventServiceProvider.php | 4 +- .../Budget/BudgetLimitRepository.php | 122 +++++++++--------- .../Budget/BudgetLimitRepositoryInterface.php | 2 +- app/Repositories/Budget/BudgetRepository.php | 4 +- .../Enrichments/BudgetLimitEnrichment.php | 24 ++-- composer.lock | 28 ++-- config/firefly.php | 4 +- package-lock.json | 26 ++-- 65 files changed, 416 insertions(+), 408 deletions(-) diff --git a/app/Api/V1/Controllers/Models/Budget/StoreController.php b/app/Api/V1/Controllers/Models/Budget/StoreController.php index 3e4954f474..66a8f36153 100644 --- a/app/Api/V1/Controllers/Models/Budget/StoreController.php +++ b/app/Api/V1/Controllers/Models/Budget/StoreController.php @@ -67,24 +67,24 @@ class StoreController extends Controller */ public function store(StoreRequest $request): JsonResponse { - $data = $request->getAll(); - $data['fire_webhooks'] = $data['fire_webhooks'] ?? true; - $budget = $this->repository->store($data); + $data = $request->getAll(); + $data['fire_webhooks'] ??= true; + $budget = $this->repository->store($data); $budget->refresh(); - $manager = $this->getManager(); + $manager = $this->getManager(); // enrich /** @var User $admin */ - $admin = auth()->user(); - $enrichment = new BudgetEnrichment(); + $admin = auth()->user(); + $enrichment = new BudgetEnrichment(); $enrichment->setUser($admin); - $budget = $enrichment->enrichSingle($budget); + $budget = $enrichment->enrichSingle($budget); /** @var BudgetTransformer $transformer */ $transformer = app(BudgetTransformer::class); $transformer->setParameters($this->parameters); - $resource = new Item($budget, $transformer, 'budgets'); + $resource = new Item($budget, $transformer, 'budgets'); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } diff --git a/app/Api/V1/Controllers/Models/Budget/UpdateController.php b/app/Api/V1/Controllers/Models/Budget/UpdateController.php index 8ec009b7e1..54bddb72a6 100644 --- a/app/Api/V1/Controllers/Models/Budget/UpdateController.php +++ b/app/Api/V1/Controllers/Models/Budget/UpdateController.php @@ -59,23 +59,23 @@ class UpdateController extends Controller public function update(UpdateRequest $request, Budget $budget): JsonResponse { - $data = $request->getAll(); - $data['fire_webhooks'] = $data['fire_webhooks'] ?? true; - $budget = $this->repository->update($budget, $data); - $manager = $this->getManager(); + $data = $request->getAll(); + $data['fire_webhooks'] ??= true; + $budget = $this->repository->update($budget, $data); + $manager = $this->getManager(); // enrich /** @var User $admin */ - $admin = auth()->user(); - $enrichment = new BudgetEnrichment(); + $admin = auth()->user(); + $enrichment = new BudgetEnrichment(); $enrichment->setUser($admin); - $budget = $enrichment->enrichSingle($budget); + $budget = $enrichment->enrichSingle($budget); /** @var BudgetTransformer $transformer */ $transformer = app(BudgetTransformer::class); $transformer->setParameters($this->parameters); - $resource = new Item($budget, $transformer, 'budgets'); + $resource = new Item($budget, $transformer, 'budgets'); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/StoreController.php b/app/Api/V1/Controllers/Models/BudgetLimit/StoreController.php index 910502662c..3b16016efa 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/StoreController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/StoreController.php @@ -67,27 +67,27 @@ class StoreController extends Controller */ public function store(StoreRequest $request, Budget $budget): JsonResponse { - $data = $request->getAll(); - $data['start_date'] = $data['start']; - $data['end_date'] = $data['end']; - $data['fire_webhooks'] = $data['fire_webhooks'] ?? true; - $data['budget_id'] = $budget->id; + $data = $request->getAll(); + $data['start_date'] = $data['start']; + $data['end_date'] = $data['end']; + $data['fire_webhooks'] ??= true; + $data['budget_id'] = $budget->id; - $budgetLimit = $this->blRepository->store($data); - $manager = $this->getManager(); + $budgetLimit = $this->blRepository->store($data); + $manager = $this->getManager(); // enrich /** @var User $admin */ - $admin = auth()->user(); - $enrichment = new BudgetLimitEnrichment(); + $admin = auth()->user(); + $enrichment = new BudgetLimitEnrichment(); $enrichment->setUser($admin); - $budgetLimit = $enrichment->enrichSingle($budgetLimit); + $budgetLimit = $enrichment->enrichSingle($budgetLimit); /** @var BudgetLimitTransformer $transformer */ - $transformer = app(BudgetLimitTransformer::class); + $transformer = app(BudgetLimitTransformer::class); $transformer->setParameters($this->parameters); - $resource = new Item($budgetLimit, $transformer, 'budget_limits'); + $resource = new Item($budgetLimit, $transformer, 'budget_limits'); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php b/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php index cca3953140..3517062e2b 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php @@ -77,7 +77,7 @@ class UpdateController extends Controller throw new FireflyException('20028: The budget limit does not belong to the budget.'); } $data = $request->getAll(); - $data['fire_webhooks'] = $data['fire_webhooks'] ?? true; + $data['fire_webhooks'] ??= true; $data['budget_id'] = $budget->id; $budgetLimit = $this->blRepository->update($budgetLimit, $data); $manager = $this->getManager(); diff --git a/app/Api/V1/Requests/Models/Budget/StoreRequest.php b/app/Api/V1/Requests/Models/Budget/StoreRequest.php index fc17d164dd..fd36873cbb 100644 --- a/app/Api/V1/Requests/Models/Budget/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Budget/StoreRequest.php @@ -48,20 +48,20 @@ class StoreRequest extends FormRequest public function getAll(): array { $fields = [ - 'name' => ['name', 'convertString'], - 'active' => ['active', 'boolean'], - 'order' => ['active', 'convertInteger'], - 'notes' => ['notes', 'convertString'], + 'name' => ['name', 'convertString'], + 'active' => ['active', 'boolean'], + 'order' => ['active', 'convertInteger'], + 'notes' => ['notes', 'convertString'], // auto budget currency: - 'currency_id' => ['auto_budget_currency_id', 'convertInteger'], - 'currency_code' => ['auto_budget_currency_code', 'convertString'], - 'auto_budget_type' => ['auto_budget_type', 'convertString'], - 'auto_budget_amount' => ['auto_budget_amount', 'convertString'], - 'auto_budget_period' => ['auto_budget_period', 'convertString'], + 'currency_id' => ['auto_budget_currency_id', 'convertInteger'], + 'currency_code' => ['auto_budget_currency_code', 'convertString'], + 'auto_budget_type' => ['auto_budget_type', 'convertString'], + 'auto_budget_amount' => ['auto_budget_amount', 'convertString'], + 'auto_budget_period' => ['auto_budget_period', 'convertString'], // webhooks - 'fire_webhooks' => ['fire_webhooks','boolean'] + 'fire_webhooks' => ['fire_webhooks', 'boolean'], ]; return $this->getAllData($fields); @@ -73,15 +73,15 @@ class StoreRequest extends FormRequest public function rules(): array { return [ - 'name' => 'required|min:1|max:255|uniqueObjectForUser:budgets,name', - 'active' => [new IsBoolean()], - 'currency_id' => 'exists:transaction_currencies,id', - 'currency_code' => 'exists:transaction_currencies,code', - 'notes' => 'nullable|min:1|max:32768', + 'name' => 'required|min:1|max:255|uniqueObjectForUser:budgets,name', + 'active' => [new IsBoolean()], + 'currency_id' => 'exists:transaction_currencies,id', + 'currency_code' => 'exists:transaction_currencies,code', + 'notes' => 'nullable|min:1|max:32768', // auto budget info - 'auto_budget_type' => 'in:reset,rollover,adjusted,none', - 'auto_budget_amount' => ['required_if:auto_budget_type,reset', 'required_if:auto_budget_type,rollover', 'required_if:auto_budget_type,adjusted', new IsValidPositiveAmount()], - 'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly|required_if:auto_budget_type,reset|required_if:auto_budget_type,rollover|required_if:auto_budget_type,adjusted', + 'auto_budget_type' => 'in:reset,rollover,adjusted,none', + 'auto_budget_amount' => ['required_if:auto_budget_type,reset', 'required_if:auto_budget_type,rollover', 'required_if:auto_budget_type,adjusted', new IsValidPositiveAmount()], + 'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly|required_if:auto_budget_type,reset|required_if:auto_budget_type,rollover|required_if:auto_budget_type,adjusted', // webhooks 'fire_webhooks' => [new IsBoolean()], diff --git a/app/Api/V1/Requests/Models/Budget/UpdateRequest.php b/app/Api/V1/Requests/Models/Budget/UpdateRequest.php index 3a89dbaa15..870cc3294f 100644 --- a/app/Api/V1/Requests/Models/Budget/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Budget/UpdateRequest.php @@ -50,18 +50,18 @@ class UpdateRequest extends FormRequest { // this is the way: $fields = [ - 'name' => ['name', 'convertString'], - 'active' => ['active', 'boolean'], - 'order' => ['order', 'convertInteger'], - 'notes' => ['notes', 'convertString'], - 'currency_id' => ['auto_budget_currency_id', 'convertInteger'], - 'currency_code' => ['auto_budget_currency_code', 'convertString'], - 'auto_budget_type' => ['auto_budget_type', 'convertString'], - 'auto_budget_amount' => ['auto_budget_amount', 'convertString'], - 'auto_budget_period' => ['auto_budget_period', 'convertString'], + 'name' => ['name', 'convertString'], + 'active' => ['active', 'boolean'], + 'order' => ['order', 'convertInteger'], + 'notes' => ['notes', 'convertString'], + 'currency_id' => ['auto_budget_currency_id', 'convertInteger'], + 'currency_code' => ['auto_budget_currency_code', 'convertString'], + 'auto_budget_type' => ['auto_budget_type', 'convertString'], + 'auto_budget_amount' => ['auto_budget_amount', 'convertString'], + 'auto_budget_period' => ['auto_budget_period', 'convertString'], // webhooks - 'fire_webhooks' => ['fire_webhooks','boolean'] + 'fire_webhooks' => ['fire_webhooks', 'boolean'], ]; $allData = $this->getAllData($fields); if (array_key_exists('auto_budget_type', $allData)) { @@ -86,14 +86,14 @@ class UpdateRequest extends FormRequest $budget = $this->route()->parameter('budget'); return [ - 'name' => sprintf('min:1|max:100|uniqueObjectForUser:budgets,name,%d', $budget->id), - 'active' => [new IsBoolean()], - 'notes' => 'nullable|min:1|max:32768', - 'auto_budget_type' => 'in:reset,rollover,adjusted,none', - 'auto_budget_currency_id' => 'exists:transaction_currencies,id', - 'auto_budget_currency_code' => 'exists:transaction_currencies,code', - 'auto_budget_amount' => ['nullable', new IsValidPositiveAmount()], - 'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly', + 'name' => sprintf('min:1|max:100|uniqueObjectForUser:budgets,name,%d', $budget->id), + 'active' => [new IsBoolean()], + 'notes' => 'nullable|min:1|max:32768', + 'auto_budget_type' => 'in:reset,rollover,adjusted,none', + 'auto_budget_currency_id' => 'exists:transaction_currencies,id', + 'auto_budget_currency_code' => 'exists:transaction_currencies,code', + 'auto_budget_amount' => ['nullable', new IsValidPositiveAmount()], + 'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly', // webhooks 'fire_webhooks' => [new IsBoolean()], diff --git a/app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php b/app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php index 2afabc38f3..1a705223de 100644 --- a/app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php +++ b/app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php @@ -25,9 +25,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\BudgetLimit; use Carbon\Carbon; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Factory\TransactionCurrencyFactory; -use FireflyIII\Models\Budget; use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Facades\Amount; @@ -69,12 +67,12 @@ class StoreRequest extends FormRequest public function rules(): array { return [ - 'start' => 'required|before:end|date', - 'end' => 'required|after:start|date', - 'amount' => ['required', new IsValidPositiveAmount()], - 'currency_id' => 'numeric|exists:transaction_currencies,id', - 'currency_code' => 'min:3|max:51|exists:transaction_currencies,code', - 'notes' => 'nullable|min:0|max:32768', + 'start' => 'required|before:end|date', + 'end' => 'required|after:start|date', + 'amount' => ['required', new IsValidPositiveAmount()], + 'currency_id' => 'numeric|exists:transaction_currencies,id', + 'currency_code' => 'min:3|max:51|exists:transaction_currencies,code', + 'notes' => 'nullable|min:0|max:32768', // webhooks 'fire_webhooks' => [new IsBoolean()], @@ -86,36 +84,36 @@ class StoreRequest extends FormRequest */ public function withValidator(Validator $validator): void { - $budget = $this->route()->parameter('budget'); + $budget = $this->route()->parameter('budget'); $validator->after( static function (Validator $validator) use ($budget): void { - if(0 !== count($validator->failed())) { + if (0 !== count($validator->failed())) { return; } - $data = $validator->getData(); + $data = $validator->getData(); // if no currency has been provided, use the user's default currency: /** @var TransactionCurrencyFactory $factory */ - $factory = app(TransactionCurrencyFactory::class); - $currency = $factory->find($data['currency_id'] ?? null, $data['currency_code'] ?? null); + $factory = app(TransactionCurrencyFactory::class); + $currency = $factory->find($data['currency_id'] ?? null, $data['currency_code'] ?? null); if (null === $currency) { $currency = Amount::getPrimaryCurrency(); } - $currency->enabled = true; + $currency->enabled = true; $currency->save(); // validator already concluded start and end are valid dates: - $start = Carbon::parse($data['start'], config('app.timezone')); - $end = Carbon::parse($data['end'], config('app.timezone')); + $start = Carbon::parse($data['start'], config('app.timezone')); + $end = Carbon::parse($data['end'], config('app.timezone')); // find limit with same date range and currency. - $limit = $budget->budgetlimits() - ->where('budget_limits.start_date', $start->format('Y-m-d')) - ->where('budget_limits.end_date', $end->format('Y-m-d')) - ->where('budget_limits.transaction_currency_id', $currency->id) - ->first(['budget_limits.*']) + $limit = $budget->budgetlimits() + ->where('budget_limits.start_date', $start->format('Y-m-d')) + ->where('budget_limits.end_date', $end->format('Y-m-d')) + ->where('budget_limits.transaction_currency_id', $currency->id) + ->first(['budget_limits.*']) ; - if(null !== $limit) { + if (null !== $limit) { $validator->errors()->add('start', trans('validation.limit_exists')); } } diff --git a/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php b/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php index 4f877b4865..5262ab4427 100644 --- a/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php @@ -47,15 +47,15 @@ class UpdateRequest extends FormRequest public function getAll(): array { $fields = [ - 'start' => ['start', 'date'], - 'end' => ['end', 'date'], - 'amount' => ['amount', 'convertString'], - 'currency_id' => ['currency_id', 'convertInteger'], - 'currency_code' => ['currency_code', 'convertString'], - 'notes' => ['notes', 'stringWithNewlines'], + 'start' => ['start', 'date'], + 'end' => ['end', 'date'], + 'amount' => ['amount', 'convertString'], + 'currency_id' => ['currency_id', 'convertInteger'], + 'currency_code' => ['currency_code', 'convertString'], + 'notes' => ['notes', 'stringWithNewlines'], // webhooks - 'fire_webhooks' => ['fire_webhooks','boolean'] + 'fire_webhooks' => ['fire_webhooks', 'boolean'], ]; if (false === $this->has('notes')) { // ignore notes, not submitted. @@ -71,12 +71,12 @@ class UpdateRequest extends FormRequest public function rules(): array { return [ - 'start' => 'date|after:1970-01-02|before:2038-01-17', - 'end' => 'date|after:1970-01-02|before:2038-01-17', - 'amount' => ['nullable', new IsValidPositiveAmount()], - 'currency_id' => 'numeric|exists:transaction_currencies,id', - 'currency_code' => 'min:3|max:51|exists:transaction_currencies,code', - 'notes' => 'nullable|min:0|max:32768', + 'start' => 'date|after:1970-01-02|before:2038-01-17', + 'end' => 'date|after:1970-01-02|before:2038-01-17', + 'amount' => ['nullable', new IsValidPositiveAmount()], + 'currency_id' => 'numeric|exists:transaction_currencies,id', + 'currency_code' => 'min:3|max:51|exists:transaction_currencies,code', + 'notes' => 'nullable|min:0|max:32768', // webhooks 'fire_webhooks' => [new IsBoolean()], diff --git a/app/Api/V1/Requests/Models/Transaction/StoreRequest.php b/app/Api/V1/Requests/Models/Transaction/StoreRequest.php index 6e85264031..4ad8a851cb 100644 --- a/app/Api/V1/Requests/Models/Transaction/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Transaction/StoreRequest.php @@ -183,7 +183,7 @@ class StoreRequest extends FormRequest // basic fields for group: 'group_title' => 'min:1|max:1000|nullable', 'error_if_duplicate_hash' => [new IsBoolean()], - 'fire_webhooks' => [new IsBoolean()], + 'fire_webhooks' => [new IsBoolean()], 'apply_rules' => [new IsBoolean()], // location rules diff --git a/app/Handlers/Observer/BudgetLimitObserver.php b/app/Handlers/Observer/BudgetLimitObserver.php index 4fb16a06fa..3c6558f0e9 100644 --- a/app/Handlers/Observer/BudgetLimitObserver.php +++ b/app/Handlers/Observer/BudgetLimitObserver.php @@ -51,7 +51,7 @@ class BudgetLimitObserver if (true === $singleton->getPreference('fire_webhooks_bl_store')) { - $user = $budgetLimit->budget->user; + $user = $budgetLimit->budget->user; /** @var MessageGeneratorInterface $engine */ $engine = app(MessageGeneratorInterface::class); @@ -75,7 +75,7 @@ class BudgetLimitObserver $userCurrency = app('amount')->getPrimaryCurrencyByUserGroup($budgetLimit->budget->user->userGroup); $budgetLimit->native_amount = null; if ($budgetLimit->transactionCurrency->id !== $userCurrency->id) { - $converter = new ExchangeRateConverter(); + $converter = new ExchangeRateConverter(); $converter->setUserGroup($budgetLimit->budget->user->userGroup); $converter->setIgnoreSettings(true); $budgetLimit->native_amount = $converter->convert($budgetLimit->transactionCurrency, $userCurrency, today(), $budgetLimit->amount); @@ -94,7 +94,7 @@ class BudgetLimitObserver $singleton = PreferencesSingleton::getInstance(); if (true === $singleton->getPreference('fire_webhooks_bl_update')) { - $user = $budgetLimit->budget->user; + $user = $budgetLimit->budget->user; /** @var MessageGeneratorInterface $engine */ $engine = app(MessageGeneratorInterface::class); diff --git a/app/Handlers/Observer/BudgetObserver.php b/app/Handlers/Observer/BudgetObserver.php index 9030c4f955..2a78e1f4be 100644 --- a/app/Handlers/Observer/BudgetObserver.php +++ b/app/Handlers/Observer/BudgetObserver.php @@ -51,7 +51,7 @@ class BudgetObserver if (true === $singleton->getPreference('fire_webhooks_budget_create')) { // fire event. - $user = $budget->user; + $user = $budget->user; /** @var MessageGeneratorInterface $engine */ $engine = app(MessageGeneratorInterface::class); @@ -72,7 +72,7 @@ class BudgetObserver $singleton = PreferencesSingleton::getInstance(); if (true === $singleton->getPreference('fire_webhooks_budget_update')) { - $user = $budget->user; + $user = $budget->user; /** @var MessageGeneratorInterface $engine */ $engine = app(MessageGeneratorInterface::class); @@ -89,10 +89,10 @@ class BudgetObserver { Log::debug('Observe "deleting" of a budget.'); - $user = $budget->user; + $user = $budget->user; /** @var MessageGeneratorInterface $engine */ - $engine = app(MessageGeneratorInterface::class); + $engine = app(MessageGeneratorInterface::class); $engine->setUser($user); $engine->setObjects(new Collection()->push($budget)); $engine->setTrigger(WebhookTrigger::DESTROY_BUDGET); @@ -100,7 +100,7 @@ class BudgetObserver Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__)); event(new RequestedSendWebhookMessages()); - $repository = app(AttachmentRepositoryInterface::class); + $repository = app(AttachmentRepositoryInterface::class); $repository->setUser($budget->user); /** @var Attachment $attachment */ diff --git a/app/Models/Account.php b/app/Models/Account.php index aedc24f00b..e76ba418ee 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -50,9 +50,9 @@ class Account extends Model use ReturnsIntegerUserIdTrait; use SoftDeletes; - protected $fillable = ['user_id', 'user_group_id', 'account_type_id', 'name', 'active', 'virtual_balance', 'iban', 'native_virtual_balance']; + protected $fillable = ['user_id', 'user_group_id', 'account_type_id', 'name', 'active', 'virtual_balance', 'iban', 'native_virtual_balance']; - protected $hidden = ['encrypted']; + protected $hidden = ['encrypted']; private bool $joinedAccountTypes = false; /** @@ -66,10 +66,10 @@ class Account extends Model $accountId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|Account $account */ - $account = $user->accounts()->with(['accountType'])->find($accountId); + $account = $user->accounts()->with(['accountType'])->find($accountId); if (null !== $account) { return $account; } @@ -126,7 +126,7 @@ class Account extends Model public function setVirtualBalanceAttribute(mixed $value): void { - $value = (string)$value; + $value = (string)$value; if ('' === $value) { $value = null; } @@ -146,7 +146,7 @@ class Account extends Model protected function accountId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } @@ -158,8 +158,9 @@ class Account extends Model return Attribute::make(get: function () { /** @var null|AccountMeta $metaValue */ $metaValue = $this->accountMeta() - ->where('name', 'account_number') - ->first(); + ->where('name', 'account_number') + ->first() + ; return null !== $metaValue ? $metaValue->data : ''; }); @@ -176,7 +177,7 @@ class Account extends Model protected function accountTypeId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } @@ -220,14 +221,14 @@ class Account extends Model protected function iban(): Attribute { return Attribute::make( - get: static fn($value) => null === $value ? null : trim(str_replace(' ', '', (string)$value)), + get: static fn ($value) => null === $value ? null : trim(str_replace(' ', '', (string)$value)), ); } protected function order(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } @@ -237,7 +238,7 @@ class Account extends Model protected function virtualBalance(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } } diff --git a/app/Models/AccountMeta.php b/app/Models/AccountMeta.php index ef87a0a508..a91e5eb778 100644 --- a/app/Models/AccountMeta.php +++ b/app/Models/AccountMeta.php @@ -27,6 +27,7 @@ use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; + use function Safe\json_decode; use function Safe\json_encode; @@ -52,6 +53,6 @@ class AccountMeta extends Model protected function data(): Attribute { - return Attribute::make(get: fn(mixed $value) => (string)json_decode((string)$value, true), set: fn(mixed $value) => ['data' => json_encode($value)]); + return Attribute::make(get: fn (mixed $value) => (string)json_decode((string)$value, true), set: fn (mixed $value) => ['data' => json_encode($value)]); } } diff --git a/app/Models/AccountType.php b/app/Models/AccountType.php index b147593f38..d4b82b45fd 100644 --- a/app/Models/AccountType.php +++ b/app/Models/AccountType.php @@ -34,39 +34,39 @@ class AccountType extends Model #[Deprecated] /** @deprecated */ - public const string ASSET = 'Asset account'; + public const string ASSET = 'Asset account'; #[Deprecated] /** @deprecated */ - public const string BENEFICIARY = 'Beneficiary account'; + public const string BENEFICIARY = 'Beneficiary account'; #[Deprecated] /** @deprecated */ - public const string CASH = 'Cash account'; + public const string CASH = 'Cash account'; #[Deprecated] /** @deprecated */ - public const string CREDITCARD = 'Credit card'; + public const string CREDITCARD = 'Credit card'; #[Deprecated] /** @deprecated */ - public const string DEBT = 'Debt'; + public const string DEBT = 'Debt'; #[Deprecated] /** @deprecated */ - public const string DEFAULT = 'Default account'; + public const string DEFAULT = 'Default account'; #[Deprecated] /** @deprecated */ - public const string EXPENSE = 'Expense account'; + public const string EXPENSE = 'Expense account'; #[Deprecated] /** @deprecated */ - public const string IMPORT = 'Import account'; + public const string IMPORT = 'Import account'; #[Deprecated] /** @deprecated */ - public const string INITIAL_BALANCE = 'Initial balance account'; + public const string INITIAL_BALANCE = 'Initial balance account'; #[Deprecated] /** @deprecated */ @@ -74,27 +74,27 @@ class AccountType extends Model #[Deprecated] /** @deprecated */ - public const string LOAN = 'Loan'; + public const string LOAN = 'Loan'; #[Deprecated] /** @deprecated */ - public const string MORTGAGE = 'Mortgage'; + public const string MORTGAGE = 'Mortgage'; #[Deprecated] /** @deprecated */ - public const string RECONCILIATION = 'Reconciliation account'; + public const string RECONCILIATION = 'Reconciliation account'; #[Deprecated] /** @deprecated */ - public const string REVENUE = 'Revenue account'; + public const string REVENUE = 'Revenue account'; protected $casts - = [ + = [ 'created_at' => 'datetime', 'updated_at' => 'datetime', ]; - protected $fillable = ['type']; + protected $fillable = ['type']; public function accounts(): HasMany { diff --git a/app/Models/Attachment.php b/app/Models/Attachment.php index dd468558ab..0323697cb8 100644 --- a/app/Models/Attachment.php +++ b/app/Models/Attachment.php @@ -56,10 +56,10 @@ class Attachment extends Model $attachmentId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|Attachment $attachment */ - $attachment = $user->attachments()->find($attachmentId); + $attachment = $user->attachments()->find($attachmentId); if (null !== $attachment) { return $attachment; } @@ -100,7 +100,7 @@ class Attachment extends Model protected function attachableId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } diff --git a/app/Models/AuditLogEntry.php b/app/Models/AuditLogEntry.php index baf532bfb0..fded5ca815 100644 --- a/app/Models/AuditLogEntry.php +++ b/app/Models/AuditLogEntry.php @@ -48,7 +48,7 @@ class AuditLogEntry extends Model protected function auditableId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } @@ -66,7 +66,7 @@ class AuditLogEntry extends Model protected function changerId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/AutoBudget.php b/app/Models/AutoBudget.php index d1aa6475c9..73dad3c6aa 100644 --- a/app/Models/AutoBudget.php +++ b/app/Models/AutoBudget.php @@ -45,17 +45,17 @@ class AutoBudget extends Model #[Deprecated] /** @deprecated */ - public const int AUTO_BUDGET_RESET = 1; + public const int AUTO_BUDGET_RESET = 1; #[Deprecated] /** @deprecated */ public const int AUTO_BUDGET_ROLLOVER = 2; protected $casts - = [ + = [ 'amount' => 'string', 'native_amount' => 'string', ]; - protected $fillable = ['budget_id', 'amount', 'period', 'native_amount']; + protected $fillable = ['budget_id', 'amount', 'period', 'native_amount']; public function budget(): BelongsTo { @@ -70,14 +70,14 @@ class AutoBudget extends Model protected function amount(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } protected function budgetId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } @@ -91,7 +91,7 @@ class AutoBudget extends Model protected function transactionCurrencyId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/AvailableBudget.php b/app/Models/AvailableBudget.php index f3f63b4e06..109abd0307 100644 --- a/app/Models/AvailableBudget.php +++ b/app/Models/AvailableBudget.php @@ -55,10 +55,10 @@ class AvailableBudget extends Model $availableBudgetId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|AvailableBudget $availableBudget */ - $availableBudget = $user->availableBudgets()->find($availableBudgetId); + $availableBudget = $user->availableBudgets()->find($availableBudgetId); if (null !== $availableBudget) { return $availableBudget; } @@ -80,7 +80,7 @@ class AvailableBudget extends Model protected function amount(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } @@ -103,23 +103,23 @@ class AvailableBudget extends Model protected function endDate(): Attribute { return Attribute::make( - get: fn(string $value) => Carbon::parse($value), - set: fn(Carbon $value) => $value->format('Y-m-d'), + get: fn (string $value) => Carbon::parse($value), + set: fn (Carbon $value) => $value->format('Y-m-d'), ); } protected function startDate(): Attribute { return Attribute::make( - get: fn(string $value) => Carbon::parse($value), - set: fn(Carbon $value) => $value->format('Y-m-d'), + get: fn (string $value) => Carbon::parse($value), + set: fn (Carbon $value) => $value->format('Y-m-d'), ); } protected function transactionCurrencyId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/Bill.php b/app/Models/Bill.php index a84552ba82..b57d98df1d 100644 --- a/app/Models/Bill.php +++ b/app/Models/Bill.php @@ -46,7 +46,7 @@ class Bill extends Model use SoftDeletes; protected $fillable - = [ + = [ 'name', 'match', 'amount_min', @@ -81,10 +81,10 @@ class Bill extends Model $billId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|Bill $bill */ - $bill = $user->bills()->find($billId); + $bill = $user->bills()->find($billId); if (null !== $bill) { return $bill; } @@ -151,7 +151,7 @@ class Bill extends Model protected function amountMax(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } @@ -161,7 +161,7 @@ class Bill extends Model protected function amountMin(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } @@ -189,7 +189,7 @@ class Bill extends Model protected function order(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } @@ -199,14 +199,14 @@ class Bill extends Model protected function skip(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } protected function transactionCurrencyId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/Budget.php b/app/Models/Budget.php index 1a1c2bb9dc..4375c6c2a7 100644 --- a/app/Models/Budget.php +++ b/app/Models/Budget.php @@ -46,7 +46,7 @@ class Budget extends Model protected $fillable = ['user_id', 'user_group_id', 'name', 'active', 'order', 'user_group_id']; - protected $hidden = ['encrypted']; + protected $hidden = ['encrypted']; /** * Route binder. Converts the key in the URL to the specified object (or throw 404). @@ -59,10 +59,10 @@ class Budget extends Model $budgetId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|Budget $budget */ - $budget = $user->budgets()->find($budgetId); + $budget = $user->budgets()->find($budgetId); if (null !== $budget) { return $budget; } @@ -125,7 +125,7 @@ class Budget extends Model protected function order(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/BudgetLimit.php b/app/Models/BudgetLimit.php index a646d4d1e2..66f12753c6 100644 --- a/app/Models/BudgetLimit.php +++ b/app/Models/BudgetLimit.php @@ -50,9 +50,10 @@ class BudgetLimit extends Model if (auth()->check()) { $budgetLimitId = (int)$value; $budgetLimit = self::where('budget_limits.id', $budgetLimitId) - ->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') - ->where('budgets.user_id', auth()->user()->id) - ->first(['budget_limits.*']); + ->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') + ->where('budgets.user_id', auth()->user()->id) + ->first(['budget_limits.*']) + ; if (null !== $budgetLimit) { return $budgetLimit; } @@ -85,14 +86,14 @@ class BudgetLimit extends Model protected function amount(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } protected function budgetId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } @@ -112,7 +113,7 @@ class BudgetLimit extends Model protected function transactionCurrencyId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/Category.php b/app/Models/Category.php index 1726c1110c..018fbdbef4 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -45,7 +45,7 @@ class Category extends Model protected $fillable = ['user_id', 'user_group_id', 'name']; - protected $hidden = ['encrypted']; + protected $hidden = ['encrypted']; /** * Route binder. Converts the key in the URL to the specified object (or throw 404). @@ -58,10 +58,10 @@ class Category extends Model $categoryId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|Category $category */ - $category = $user->categories()->find($categoryId); + $category = $user->categories()->find($categoryId); if (null !== $category) { return $category; } diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index 49e7559236..3fd2d82a19 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -27,6 +27,7 @@ use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; + use function Safe\json_decode; use function Safe\json_encode; @@ -51,6 +52,6 @@ class Configuration extends Model */ protected function data(): Attribute { - return Attribute::make(get: fn($value) => json_decode((string)$value), set: fn($value) => ['data' => json_encode($value)]); + return Attribute::make(get: fn ($value) => json_decode((string)$value), set: fn ($value) => ['data' => json_encode($value)]); } } diff --git a/app/Models/CurrencyExchangeRate.php b/app/Models/CurrencyExchangeRate.php index fc919dbf80..c484ae2cc5 100644 --- a/app/Models/CurrencyExchangeRate.php +++ b/app/Models/CurrencyExchangeRate.php @@ -73,28 +73,28 @@ class CurrencyExchangeRate extends Model protected function fromCurrencyId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } protected function rate(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } protected function toCurrencyId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } protected function userRate(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } } diff --git a/app/Models/GroupMembership.php b/app/Models/GroupMembership.php index e16178fa9b..52f6cf9b24 100644 --- a/app/Models/GroupMembership.php +++ b/app/Models/GroupMembership.php @@ -66,7 +66,7 @@ class GroupMembership extends Model protected function userRoleId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/InvitedUser.php b/app/Models/InvitedUser.php index 272096faa8..81e945b5e9 100644 --- a/app/Models/InvitedUser.php +++ b/app/Models/InvitedUser.php @@ -48,7 +48,7 @@ class InvitedUser extends Model $attemptId = (int)$value; /** @var null|InvitedUser $attempt */ - $attempt = self::find($attemptId); + $attempt = self::find($attemptId); if (null !== $attempt) { return $attempt; } diff --git a/app/Models/Location.php b/app/Models/Location.php index 2e971b6f67..b8f2b31151 100644 --- a/app/Models/Location.php +++ b/app/Models/Location.php @@ -81,7 +81,7 @@ class Location extends Model protected function locatableId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/Note.php b/app/Models/Note.php index 71d0531976..2adeacc457 100644 --- a/app/Models/Note.php +++ b/app/Models/Note.php @@ -56,7 +56,7 @@ class Note extends Model protected function noteableId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/ObjectGroup.php b/app/Models/ObjectGroup.php index 71a4d9b9ba..189e543a41 100644 --- a/app/Models/ObjectGroup.php +++ b/app/Models/ObjectGroup.php @@ -51,8 +51,9 @@ class ObjectGroup extends Model $objectGroupId = (int)$value; /** @var null|ObjectGroup $objectGroup */ - $objectGroup = self::where('object_groups.id', $objectGroupId) - ->where('object_groups.user_id', auth()->user()->id)->first(); + $objectGroup = self::where('object_groups.id', $objectGroupId) + ->where('object_groups.user_id', auth()->user()->id)->first() + ; if (null !== $objectGroup) { return $objectGroup; } @@ -104,7 +105,7 @@ class ObjectGroup extends Model protected function order(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index c493198b09..d4eb25a787 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -54,9 +54,10 @@ class PiggyBank extends Model if (auth()->check()) { $piggyBankId = (int)$value; $piggyBank = self::where('piggy_banks.id', $piggyBankId) - ->leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id') - ->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id') - ->where('accounts.user_id', auth()->user()->id)->first(['piggy_banks.*']); + ->leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id') + ->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id') + ->where('accounts.user_id', auth()->user()->id)->first(['piggy_banks.*']) + ; if (null !== $piggyBank) { return $piggyBank; } @@ -122,7 +123,7 @@ class PiggyBank extends Model protected function accountId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } @@ -145,7 +146,7 @@ class PiggyBank extends Model protected function order(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } @@ -155,7 +156,7 @@ class PiggyBank extends Model protected function targetAmount(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } } diff --git a/app/Models/PiggyBankEvent.php b/app/Models/PiggyBankEvent.php index 81578a9544..3395e3df53 100644 --- a/app/Models/PiggyBankEvent.php +++ b/app/Models/PiggyBankEvent.php @@ -38,7 +38,7 @@ class PiggyBankEvent extends Model protected $fillable = ['piggy_bank_id', 'transaction_journal_id', 'date', 'date_tz', 'amount', 'native_amount']; - protected $hidden = ['amount_encrypted']; + protected $hidden = ['amount_encrypted']; public function piggyBank(): BelongsTo { @@ -64,7 +64,7 @@ class PiggyBankEvent extends Model protected function amount(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } @@ -82,7 +82,7 @@ class PiggyBankEvent extends Model protected function piggyBankId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/PiggyBankRepetition.php b/app/Models/PiggyBankRepetition.php index fa6d9823ed..41af799266 100644 --- a/app/Models/PiggyBankRepetition.php +++ b/app/Models/PiggyBankRepetition.php @@ -68,7 +68,7 @@ class PiggyBankRepetition extends Model protected function currentAmount(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } @@ -81,7 +81,7 @@ class PiggyBankRepetition extends Model protected function piggyBankId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } @@ -97,11 +97,12 @@ class PiggyBankRepetition extends Model $q->orWhereNull('start_date'); } ) - ->where( - static function (EloquentBuilder $q) use ($date): void { - $q->where('target_date', '>=', $date->format('Y-m-d 00:00:00')); - $q->orWhereNull('target_date'); - } - ); + ->where( + static function (EloquentBuilder $q) use ($date): void { + $q->where('target_date', '>=', $date->format('Y-m-d 00:00:00')); + $q->orWhereNull('target_date'); + } + ) + ; } } diff --git a/app/Models/Preference.php b/app/Models/Preference.php index f357f17cc7..151e5bbd35 100644 --- a/app/Models/Preference.php +++ b/app/Models/Preference.php @@ -46,7 +46,7 @@ class Preference extends Model { if (auth()->check()) { /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); // some preferences do not have an administration ID. // some need it, to make sure the correct one is selected. @@ -54,8 +54,8 @@ class Preference extends Model $userGroupId = 0 === $userGroupId ? null : $userGroupId; /** @var null|Preference $preference */ - $preference = null; - $items = config('firefly.admin_specific_prefs'); + $preference = null; + $items = config('firefly.admin_specific_prefs'); if (null !== $userGroupId && in_array($value, $items, true)) { // find a preference with a specific user_group_id $preference = $user->preferences()->where('user_group_id', $userGroupId)->where('name', $value)->first(); @@ -73,7 +73,7 @@ class Preference extends Model /** @var Preference $preference */ return $preference; } - $default = config('firefly.default_preferences'); + $default = config('firefly.default_preferences'); if (array_key_exists($value, $default)) { $preference = new self(); $preference->name = $value; diff --git a/app/Models/Recurrence.php b/app/Models/Recurrence.php index 724ccdb06f..b3fa7a3a8c 100644 --- a/app/Models/Recurrence.php +++ b/app/Models/Recurrence.php @@ -46,7 +46,7 @@ class Recurrence extends Model use SoftDeletes; protected $fillable - = ['user_id', 'user_group_id', 'transaction_type_id', 'title', 'description', 'first_date', 'first_date_tz', 'repeat_until', 'repeat_until_tz', 'latest_date', 'latest_date_tz', 'repetitions', 'apply_rules', 'active']; + = ['user_id', 'user_group_id', 'transaction_type_id', 'title', 'description', 'first_date', 'first_date_tz', 'repeat_until', 'repeat_until_tz', 'latest_date', 'latest_date_tz', 'repetitions', 'apply_rules', 'active']; protected $table = 'recurrences'; @@ -61,10 +61,10 @@ class Recurrence extends Model $recurrenceId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|Recurrence $recurrence */ - $recurrence = $user->recurrences()->find($recurrenceId); + $recurrence = $user->recurrences()->find($recurrenceId); if (null !== $recurrence) { return $recurrence; } @@ -139,7 +139,7 @@ class Recurrence extends Model protected function transactionTypeId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/RecurrenceMeta.php b/app/Models/RecurrenceMeta.php index fddd2e8292..d031e0b883 100644 --- a/app/Models/RecurrenceMeta.php +++ b/app/Models/RecurrenceMeta.php @@ -37,7 +37,7 @@ class RecurrenceMeta extends Model protected $fillable = ['recurrence_id', 'name', 'value']; - protected $table = 'recurrences_meta'; + protected $table = 'recurrences_meta'; public function recurrence(): BelongsTo { @@ -58,7 +58,7 @@ class RecurrenceMeta extends Model protected function recurrenceId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/RecurrenceRepetition.php b/app/Models/RecurrenceRepetition.php index 920f8d93a1..52d8259877 100644 --- a/app/Models/RecurrenceRepetition.php +++ b/app/Models/RecurrenceRepetition.php @@ -38,7 +38,7 @@ class RecurrenceRepetition extends Model #[Deprecated] /** @deprecated */ - public const int WEEKEND_DO_NOTHING = 1; + public const int WEEKEND_DO_NOTHING = 1; #[Deprecated] /** @deprecated */ @@ -46,14 +46,14 @@ class RecurrenceRepetition extends Model #[Deprecated] /** @deprecated */ - public const int WEEKEND_TO_FRIDAY = 3; + public const int WEEKEND_TO_FRIDAY = 3; #[Deprecated] /** @deprecated */ - public const int WEEKEND_TO_MONDAY = 4; + public const int WEEKEND_TO_MONDAY = 4; protected $casts - = [ + = [ 'created_at' => 'datetime', 'updated_at' => 'datetime', 'deleted_at' => 'datetime', @@ -63,9 +63,9 @@ class RecurrenceRepetition extends Model 'weekend' => 'int', ]; - protected $fillable = ['recurrence_id', 'weekend', 'repetition_type', 'repetition_moment', 'repetition_skip']; + protected $fillable = ['recurrence_id', 'weekend', 'repetition_type', 'repetition_moment', 'repetition_skip']; - protected $table = 'recurrences_repetitions'; + protected $table = 'recurrences_repetitions'; public function recurrence(): BelongsTo { @@ -82,21 +82,21 @@ class RecurrenceRepetition extends Model protected function recurrenceId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } protected function repetitionSkip(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } protected function weekend(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/RecurrenceTransaction.php b/app/Models/RecurrenceTransaction.php index 4c1a5f6f2c..c2d91a54ad 100644 --- a/app/Models/RecurrenceTransaction.php +++ b/app/Models/RecurrenceTransaction.php @@ -40,7 +40,7 @@ class RecurrenceTransaction extends Model use SoftDeletes; protected $fillable - = [ + = [ 'recurrence_id', 'transaction_currency_id', 'foreign_currency_id', @@ -91,7 +91,7 @@ class RecurrenceTransaction extends Model protected function amount(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } @@ -110,42 +110,42 @@ class RecurrenceTransaction extends Model protected function destinationId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } protected function foreignAmount(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } protected function recurrenceId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } protected function sourceId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } protected function transactionCurrencyId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } protected function userId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/RecurrenceTransactionMeta.php b/app/Models/RecurrenceTransactionMeta.php index 5f2644b0f6..442da02559 100644 --- a/app/Models/RecurrenceTransactionMeta.php +++ b/app/Models/RecurrenceTransactionMeta.php @@ -37,7 +37,7 @@ class RecurrenceTransactionMeta extends Model protected $fillable = ['rt_id', 'name', 'value']; - protected $table = 'rt_meta'; + protected $table = 'rt_meta'; public function recurrenceTransaction(): BelongsTo { @@ -58,7 +58,7 @@ class RecurrenceTransactionMeta extends Model protected function rtId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/Rule.php b/app/Models/Rule.php index a8de1ea45b..7755e00984 100644 --- a/app/Models/Rule.php +++ b/app/Models/Rule.php @@ -55,10 +55,10 @@ class Rule extends Model $ruleId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|Rule $rule */ - $rule = $user->rules()->find($ruleId); + $rule = $user->rules()->find($ruleId); if (null !== $rule) { return $rule; } @@ -110,20 +110,20 @@ class Rule extends Model protected function description(): Attribute { - return Attribute::make(set: fn($value) => ['description' => e($value)]); + return Attribute::make(set: fn ($value) => ['description' => e($value)]); } protected function order(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } protected function ruleGroupId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/RuleAction.php b/app/Models/RuleAction.php index 3603c6cc65..0bb29a5236 100644 --- a/app/Models/RuleAction.php +++ b/app/Models/RuleAction.php @@ -80,14 +80,14 @@ class RuleAction extends Model protected function order(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } protected function ruleId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/RuleGroup.php b/app/Models/RuleGroup.php index d53155d888..99f6655f63 100644 --- a/app/Models/RuleGroup.php +++ b/app/Models/RuleGroup.php @@ -55,10 +55,10 @@ class RuleGroup extends Model $ruleGroupId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|RuleGroup $ruleGroup */ - $ruleGroup = $user->ruleGroups()->find($ruleGroupId); + $ruleGroup = $user->ruleGroups()->find($ruleGroupId); if (null !== $ruleGroup) { return $ruleGroup; } @@ -94,7 +94,7 @@ class RuleGroup extends Model protected function order(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/RuleTrigger.php b/app/Models/RuleTrigger.php index 9fedd62399..c3de8048ba 100644 --- a/app/Models/RuleTrigger.php +++ b/app/Models/RuleTrigger.php @@ -53,14 +53,14 @@ class RuleTrigger extends Model protected function order(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } protected function ruleId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/Tag.php b/app/Models/Tag.php index 22572b8a06..c9fc2f134b 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -45,7 +45,7 @@ class Tag extends Model protected $fillable = ['user_id', 'user_group_id', 'tag', 'date', 'date_tz', 'description', 'tag_mode']; - protected $hidden = ['zoomLevel', 'zoom_level', 'latitude', 'longitude']; + protected $hidden = ['zoomLevel', 'zoom_level', 'latitude', 'longitude']; /** * Route binder. Converts the key in the URL to the specified object (or throw 404). @@ -58,10 +58,10 @@ class Tag extends Model $tagId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|Tag $tag */ - $tag = $user->tags()->find($tagId); + $tag = $user->tags()->find($tagId); if (null !== $tag) { return $tag; } diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index e0b521d30f..e33cb0a1ac 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -44,7 +44,7 @@ class Transaction extends Model use SoftDeletes; protected $fillable - = [ + = [ 'account_id', 'transaction_journal_id', 'description', @@ -113,7 +113,7 @@ class Transaction extends Model protected function accountId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } @@ -151,14 +151,14 @@ class Transaction extends Model protected function amount(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } protected function balanceDirty(): Attribute { return Attribute::make( - get: static fn($value) => 1 === (int)$value, + get: static fn ($value) => 1 === (int)$value, ); } @@ -201,14 +201,14 @@ class Transaction extends Model protected function foreignAmount(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } protected function transactionJournalId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } diff --git a/app/Models/TransactionCurrency.php b/app/Models/TransactionCurrency.php index 7ecfbd18db..6f02375355 100644 --- a/app/Models/TransactionCurrency.php +++ b/app/Models/TransactionCurrency.php @@ -40,7 +40,7 @@ class TransactionCurrency extends Model public ?bool $userGroupEnabled = null; public ?bool $userGroupNative = null; - protected $fillable = ['name', 'code', 'symbol', 'decimal_places', 'enabled']; + protected $fillable = ['name', 'code', 'symbol', 'decimal_places', 'enabled']; /** * Route binder. Converts the key in the URL to the specified object (or throw 404). @@ -115,7 +115,7 @@ class TransactionCurrency extends Model protected function decimalPlaces(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/TransactionGroup.php b/app/Models/TransactionGroup.php index ec8bf0faae..81e7aac2e6 100644 --- a/app/Models/TransactionGroup.php +++ b/app/Models/TransactionGroup.php @@ -55,13 +55,14 @@ class TransactionGroup extends Model $groupId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); app('log')->debug(sprintf('User authenticated as %s', $user->email)); /** @var null|TransactionGroup $group */ - $group = $user->transactionGroups() - ->with(['transactionJournals', 'transactionJournals.transactions']) - ->where('transaction_groups.id', $groupId)->first(['transaction_groups.*']); + $group = $user->transactionGroups() + ->with(['transactionJournals', 'transactionJournals.transactions']) + ->where('transaction_groups.id', $groupId)->first(['transaction_groups.*']) + ; if (null !== $group) { app('log')->debug(sprintf('Found group #%d.', $group->id)); diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index dde97c2fce..3aa0099446 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -57,7 +57,7 @@ class TransactionJournal extends Model use SoftDeletes; protected $fillable - = [ + = [ 'user_id', 'user_group_id', 'transaction_type_id', @@ -84,10 +84,10 @@ class TransactionJournal extends Model $journalId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|TransactionJournal $journal */ - $journal = $user->transactionJournals()->where('transaction_journals.id', $journalId)->first(['transaction_journals.*']); + $journal = $user->transactionJournals()->where('transaction_journals.id', $journalId)->first(['transaction_journals.*']); if (null !== $journal) { return $journal; } @@ -230,14 +230,14 @@ class TransactionJournal extends Model protected function order(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } protected function transactionTypeId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } diff --git a/app/Models/TransactionJournalLink.php b/app/Models/TransactionJournalLink.php index 4880d15c39..ab1a40c260 100644 --- a/app/Models/TransactionJournalLink.php +++ b/app/Models/TransactionJournalLink.php @@ -46,11 +46,12 @@ class TransactionJournalLink extends Model if (auth()->check()) { $linkId = (int)$value; $link = self::where('journal_links.id', $linkId) - ->leftJoin('transaction_journals as t_a', 't_a.id', '=', 'source_id') - ->leftJoin('transaction_journals as t_b', 't_b.id', '=', 'destination_id') - ->where('t_a.user_id', auth()->user()->id) - ->where('t_b.user_id', auth()->user()->id) - ->first(['journal_links.*']); + ->leftJoin('transaction_journals as t_a', 't_a.id', '=', 'source_id') + ->leftJoin('transaction_journals as t_b', 't_b.id', '=', 'destination_id') + ->where('t_a.user_id', auth()->user()->id) + ->where('t_b.user_id', auth()->user()->id) + ->first(['journal_links.*']) + ; if (null !== $link) { return $link; } @@ -93,21 +94,21 @@ class TransactionJournalLink extends Model protected function destinationId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } protected function linkTypeId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } protected function sourceId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/TransactionJournalMeta.php b/app/Models/TransactionJournalMeta.php index 7dd67b6f86..4a748bfb8b 100644 --- a/app/Models/TransactionJournalMeta.php +++ b/app/Models/TransactionJournalMeta.php @@ -28,6 +28,7 @@ use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; + use function Safe\json_decode; use function Safe\json_encode; @@ -38,7 +39,7 @@ class TransactionJournalMeta extends Model protected $fillable = ['transaction_journal_id', 'name', 'data', 'hash']; - protected $table = 'journal_meta'; + protected $table = 'journal_meta'; public function transactionJournal(): BelongsTo { @@ -56,7 +57,7 @@ class TransactionJournalMeta extends Model protected function data(): Attribute { - return Attribute::make(get: fn($value) => json_decode((string)$value, false), set: function ($value) { + return Attribute::make(get: fn ($value) => json_decode((string)$value, false), set: function ($value) { $data = json_encode($value); return ['data' => $data, 'hash' => hash('sha256', $data)]; @@ -66,7 +67,7 @@ class TransactionJournalMeta extends Model protected function transactionJournalId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/TransactionType.php b/app/Models/TransactionType.php index 4062911fae..cac754f110 100644 --- a/app/Models/TransactionType.php +++ b/app/Models/TransactionType.php @@ -38,11 +38,11 @@ class TransactionType extends Model #[Deprecated] /** @deprecated */ - public const string DEPOSIT = 'Deposit'; + public const string DEPOSIT = 'Deposit'; #[Deprecated] /** @deprecated */ - public const string INVALID = 'Invalid'; + public const string INVALID = 'Invalid'; #[Deprecated] /** @deprecated */ @@ -50,27 +50,27 @@ class TransactionType extends Model #[Deprecated] /** @deprecated */ - public const string OPENING_BALANCE = 'Opening balance'; + public const string OPENING_BALANCE = 'Opening balance'; #[Deprecated] /** @deprecated */ - public const string RECONCILIATION = 'Reconciliation'; + public const string RECONCILIATION = 'Reconciliation'; #[Deprecated] /** @deprecated */ - public const string TRANSFER = 'Transfer'; + public const string TRANSFER = 'Transfer'; #[Deprecated] /** @deprecated */ - public const string WITHDRAWAL = 'Withdrawal'; + public const string WITHDRAWAL = 'Withdrawal'; protected $casts - = [ + = [ 'created_at' => 'datetime', 'updated_at' => 'datetime', 'deleted_at' => 'datetime', ]; - protected $fillable = ['type']; + protected $fillable = ['type']; /** * Route binder. Converts the key in the URL to the specified object (or throw 404). diff --git a/app/Models/UserGroup.php b/app/Models/UserGroup.php index 7b20a2980e..0c142b69cb 100644 --- a/app/Models/UserGroup.php +++ b/app/Models/UserGroup.php @@ -50,16 +50,16 @@ class UserGroup extends Model $userGroupId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|UserGroup $userGroup */ - $userGroup = self::find($userGroupId); + $userGroup = self::find($userGroupId); if (null === $userGroup) { throw new NotFoundHttpException(); } // need at least ready only to be aware of the user group's existence, // but owner/full role (in the group) or global owner role may overrule this. - $access = $user->hasRoleInGroupOrOwner($userGroup, UserRoleEnum::READ_ONLY) || $user->hasRole('owner'); + $access = $user->hasRoleInGroupOrOwner($userGroup, UserRoleEnum::READ_ONLY) || $user->hasRole('owner'); if ($access) { return $userGroup; } diff --git a/app/Models/Webhook.php b/app/Models/Webhook.php index 5b19fe269c..ab7e477e37 100644 --- a/app/Models/Webhook.php +++ b/app/Models/Webhook.php @@ -137,10 +137,10 @@ class Webhook extends Model $webhookId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|Webhook $webhook */ - $webhook = $user->webhooks()->find($webhookId); + $webhook = $user->webhooks()->find($webhookId); if (null !== $webhook) { return $webhook; } diff --git a/app/Models/WebhookAttempt.php b/app/Models/WebhookAttempt.php index c38fd15fe1..a2de5dac3b 100644 --- a/app/Models/WebhookAttempt.php +++ b/app/Models/WebhookAttempt.php @@ -48,10 +48,10 @@ class WebhookAttempt extends Model $attemptId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|WebhookAttempt $attempt */ - $attempt = self::find($attemptId); + $attempt = self::find($attemptId); if (null !== $attempt && $attempt->webhookMessage->webhook->user_id === $user->id) { return $attempt; } @@ -68,7 +68,7 @@ class WebhookAttempt extends Model protected function webhookMessageId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/WebhookDelivery.php b/app/Models/WebhookDelivery.php index 614a5cb3c2..33acf3b9b7 100644 --- a/app/Models/WebhookDelivery.php +++ b/app/Models/WebhookDelivery.php @@ -41,7 +41,7 @@ class WebhookDelivery extends Model protected function key(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/WebhookMessage.php b/app/Models/WebhookMessage.php index cae9a4a73f..dbccb97cbd 100644 --- a/app/Models/WebhookMessage.php +++ b/app/Models/WebhookMessage.php @@ -50,10 +50,10 @@ class WebhookMessage extends Model $messageId = (int)$value; /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var null|WebhookMessage $message */ - $message = self::find($messageId); + $message = self::find($messageId); if (null !== $message && $message->webhook->user_id === $user->id) { return $message; } @@ -89,14 +89,14 @@ class WebhookMessage extends Model protected function sent(): Attribute { return Attribute::make( - get: static fn($value) => (bool)$value, + get: static fn ($value) => (bool)$value, ); } protected function webhookId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/WebhookResponse.php b/app/Models/WebhookResponse.php index 5c8cb45311..7b3e785a73 100644 --- a/app/Models/WebhookResponse.php +++ b/app/Models/WebhookResponse.php @@ -41,7 +41,7 @@ class WebhookResponse extends Model protected function key(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/WebhookTrigger.php b/app/Models/WebhookTrigger.php index 7d9a6ea1fa..b7ccd7cfc5 100644 --- a/app/Models/WebhookTrigger.php +++ b/app/Models/WebhookTrigger.php @@ -41,7 +41,7 @@ class WebhookTrigger extends Model protected function key(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index a63903a280..1b221013f5 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -216,7 +216,5 @@ class EventServiceProvider extends ServiceProvider * Register any events for your application. */ #[Override] - public function boot(): void - { - } + public function boot(): void {} } diff --git a/app/Repositories/Budget/BudgetLimitRepository.php b/app/Repositories/Budget/BudgetLimitRepository.php index 857052b081..ab80405609 100644 --- a/app/Repositories/Budget/BudgetLimitRepository.php +++ b/app/Repositories/Budget/BudgetLimitRepository.php @@ -298,7 +298,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface, UserGroup Log::debug('No existing budget limit, create a new one'); // this is a lame trick to communicate with the observer. - $singleton = PreferencesSingleton::getInstance(); + $singleton = PreferencesSingleton::getInstance(); $singleton->setPreference('fire_webhooks_bl_store', $data['fire_webhooks'] ?? true); // or create one and return it. @@ -381,7 +381,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface, UserGroup $currency->save(); // this is a lame trick to communicate with the observer. - $singleton = PreferencesSingleton::getInstance(); + $singleton = PreferencesSingleton::getInstance(); $singleton->setPreference('fire_webhooks_bl_update', $data['fire_webhooks'] ?? true); $budgetLimit->transaction_currency_id = $currency->id; @@ -395,63 +395,63 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface, UserGroup return $budgetLimit; } -// public function updateLimitAmount(Budget $budget, Carbon $start, Carbon $end, string $amount): ?BudgetLimit -// { -// // count the limits: -// $limits = $budget->budgetlimits() -// ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00')) -// ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) -// ->count('budget_limits.*') -// ; -// Log::debug(sprintf('Found %d budget limits.', $limits)); -// -// // there might be a budget limit for these dates: -// /** @var null|BudgetLimit $limit */ -// $limit = $budget->budgetlimits() -// ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00')) -// ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) -// ->first(['budget_limits.*']) -// ; -// -// // if more than 1 limit found, delete the others: -// if ($limits > 1 && null !== $limit) { -// Log::debug(sprintf('Found more than 1, delete all except #%d', $limit->id)); -// $budget->budgetlimits() -// ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00')) -// ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) -// ->where('budget_limits.id', '!=', $limit->id)->delete() -// ; -// } -// -// // delete if amount is zero. -// // Returns 0 if the two operands are equal, -// // 1 if the left_operand is larger than the right_operand, -1 otherwise. -// if (null !== $limit && bccomp($amount, '0') <= 0) { -// Log::debug(sprintf('%s is zero, delete budget limit #%d', $amount, $limit->id)); -// $limit->delete(); -// -// return null; -// } -// // update if exists: -// if (null !== $limit) { -// Log::debug(sprintf('Existing budget limit is #%d, update this to amount %s', $limit->id, $amount)); -// $limit->amount = $amount; -// $limit->save(); -// -// return $limit; -// } -// Log::debug('No existing budget limit, create a new one'); -// // or create one and return it. -// $limit = new BudgetLimit(); -// $limit->budget()->associate($budget); -// $limit->start_date = $start->startOfDay(); -// $limit->start_date_tz = $start->format('e'); -// $limit->end_date = $end->startOfDay(); -// $limit->end_date_tz = $end->format('e'); -// $limit->amount = $amount; -// $limit->save(); -// Log::debug(sprintf('Created new budget limit with ID #%d and amount %s', $limit->id, $amount)); -// -// return $limit; -// } + // public function updateLimitAmount(Budget $budget, Carbon $start, Carbon $end, string $amount): ?BudgetLimit + // { + // // count the limits: + // $limits = $budget->budgetlimits() + // ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00')) + // ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) + // ->count('budget_limits.*') + // ; + // Log::debug(sprintf('Found %d budget limits.', $limits)); + // + // // there might be a budget limit for these dates: + // /** @var null|BudgetLimit $limit */ + // $limit = $budget->budgetlimits() + // ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00')) + // ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) + // ->first(['budget_limits.*']) + // ; + // + // // if more than 1 limit found, delete the others: + // if ($limits > 1 && null !== $limit) { + // Log::debug(sprintf('Found more than 1, delete all except #%d', $limit->id)); + // $budget->budgetlimits() + // ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00')) + // ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) + // ->where('budget_limits.id', '!=', $limit->id)->delete() + // ; + // } + // + // // delete if amount is zero. + // // Returns 0 if the two operands are equal, + // // 1 if the left_operand is larger than the right_operand, -1 otherwise. + // if (null !== $limit && bccomp($amount, '0') <= 0) { + // Log::debug(sprintf('%s is zero, delete budget limit #%d', $amount, $limit->id)); + // $limit->delete(); + // + // return null; + // } + // // update if exists: + // if (null !== $limit) { + // Log::debug(sprintf('Existing budget limit is #%d, update this to amount %s', $limit->id, $amount)); + // $limit->amount = $amount; + // $limit->save(); + // + // return $limit; + // } + // Log::debug('No existing budget limit, create a new one'); + // // or create one and return it. + // $limit = new BudgetLimit(); + // $limit->budget()->associate($budget); + // $limit->start_date = $start->startOfDay(); + // $limit->start_date_tz = $start->format('e'); + // $limit->end_date = $end->startOfDay(); + // $limit->end_date_tz = $end->format('e'); + // $limit->amount = $amount; + // $limit->save(); + // Log::debug(sprintf('Created new budget limit with ID #%d and amount %s', $limit->id, $amount)); + // + // return $limit; + // } } diff --git a/app/Repositories/Budget/BudgetLimitRepositoryInterface.php b/app/Repositories/Budget/BudgetLimitRepositoryInterface.php index 7d8ccde5bb..defb1c7d49 100644 --- a/app/Repositories/Budget/BudgetLimitRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetLimitRepositoryInterface.php @@ -81,5 +81,5 @@ interface BudgetLimitRepositoryInterface public function update(BudgetLimit $budgetLimit, array $data): BudgetLimit; - //public function updateLimitAmount(Budget $budget, Carbon $start, Carbon $end, string $amount): ?BudgetLimit; + // public function updateLimitAmount(Budget $budget, Carbon $start, Carbon $end, string $amount): ?BudgetLimit; } diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index b893334320..8aaaa40b8a 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -286,7 +286,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface Log::debug('Now in update()'); // this is a lame trick to communicate with the observer. - $singleton = PreferencesSingleton::getInstance(); + $singleton = PreferencesSingleton::getInstance(); $singleton->setPreference('fire_webhooks_budget_update', $data['fire_webhooks'] ?? true); $oldName = $budget->name; @@ -730,7 +730,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface $order = $this->getMaxOrder(); // this is a lame trick to communicate with the observer. - $singleton = PreferencesSingleton::getInstance(); + $singleton = PreferencesSingleton::getInstance(); $singleton->setPreference('fire_webhooks_budget_create', $data['fire_webhooks'] ?? true); try { diff --git a/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php b/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php index c0af1e7735..0ba9e1f1a3 100644 --- a/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php +++ b/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php @@ -95,8 +95,8 @@ class BudgetLimitEnrichment implements EnrichmentInterface private function collectIds(): void { - $this->start = $this->collection->min('start_date') ?? Carbon::now()->startOfMonth(); - $this->end = $this->collection->max('end_date') ?? Carbon::now()->endOfMonth(); + $this->start = $this->collection->min('start_date') ?? Carbon::now()->startOfMonth(); + $this->end = $this->collection->max('end_date') ?? Carbon::now()->endOfMonth(); /** @var BudgetLimit $limit */ foreach ($this->collection as $limit) { @@ -113,9 +113,10 @@ class BudgetLimitEnrichment implements EnrichmentInterface private function collectNotes(): void { $notes = Note::query()->whereIn('noteable_id', $this->ids) - ->whereNotNull('notes.text') - ->where('notes.text', '!=', '') - ->where('noteable_type', BudgetLimit::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); + ->whereNotNull('notes.text') + ->where('notes.text', '!=', '') + ->where('noteable_type', BudgetLimit::class)->get(['notes.noteable_id', 'notes.text'])->toArray() + ; foreach ($notes as $note) { $this->notes[(int)$note['noteable_id']] = (string)$note['text']; } @@ -144,12 +145,12 @@ class BudgetLimitEnrichment implements EnrichmentInterface private function collectBudgets(): void { - $budgetIds = $this->collection->pluck('budget_id')->unique()->toArray(); - $budgets = Budget::whereIn('id', $budgetIds)->get(); + $budgetIds = $this->collection->pluck('budget_id')->unique()->toArray(); + $budgets = Budget::whereIn('id', $budgetIds)->get(); $repository = app(OperationsRepository::class); $repository->setUser($this->user); - $expenses = $repository->collectExpenses($this->start, $this->end, null, $budgets, null); + $expenses = $repository->collectExpenses($this->start, $this->end, null, $budgets, null); /** @var BudgetLimit $budgetLimit */ foreach ($this->collection as $budgetLimit) { @@ -180,13 +181,13 @@ class BudgetLimitEnrichment implements EnrichmentInterface private function stringifyIds(): void { - $this->expenses = array_map(fn($first) => array_map(function ($second) { + $this->expenses = array_map(fn ($first) => array_map(function ($second) { $second['currency_id'] = (string)($second['currency_id'] ?? 0); return $second; }, $first), $this->expenses); - $this->pcExpenses = array_map(fn($first) => array_map(function ($second) { + $this->pcExpenses = array_map(fn ($first) => array_map(function ($second) { $second['currency_id'] = (string)($second['currency_id'] ?? 0); return $second; @@ -195,8 +196,9 @@ class BudgetLimitEnrichment implements EnrichmentInterface private function filterToBudget(array $expenses, int $budget): array { - $result = array_filter($expenses, fn(array $item) => (int)$item['budget_id'] === $budget); + $result = array_filter($expenses, fn (array $item) => (int)$item['budget_id'] === $budget); Log::debug(sprintf('filterToBudget for budget #%d, from %d to %d items', $budget, count($expenses), count($result))); + return $result; } } diff --git a/composer.lock b/composer.lock index 50beadca73..3bc0182e4b 100644 --- a/composer.lock +++ b/composer.lock @@ -11819,16 +11819,16 @@ }, { "name": "phpunit/phpunit", - "version": "12.3.10", + "version": "12.3.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "0d401d0df2e3c1703be425ecdc2d04f5c095938d" + "reference": "6a62f2b394e042884e4997ddc8b8db1ce56a0009" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0d401d0df2e3c1703be425ecdc2d04f5c095938d", - "reference": "0d401d0df2e3c1703be425ecdc2d04f5c095938d", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6a62f2b394e042884e4997ddc8b8db1ce56a0009", + "reference": "6a62f2b394e042884e4997ddc8b8db1ce56a0009", "shasum": "" }, "require": { @@ -11847,7 +11847,7 @@ "phpunit/php-invoker": "^6.0.0", "phpunit/php-text-template": "^5.0.0", "phpunit/php-timer": "^8.0.0", - "sebastian/cli-parser": "^4.0.0", + "sebastian/cli-parser": "^4.1.0", "sebastian/comparator": "^7.1.3", "sebastian/diff": "^7.0.0", "sebastian/environment": "^8.0.3", @@ -11896,7 +11896,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.10" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.11" }, "funding": [ { @@ -11920,7 +11920,7 @@ "type": "tidelift" } ], - "time": "2025-09-11T10:35:19+00:00" + "time": "2025-09-14T06:21:44+00:00" }, { "name": "rector/rector", @@ -11984,16 +11984,16 @@ }, { "name": "sebastian/cli-parser", - "version": "4.1.0", + "version": "4.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "8fd93be538992d556aaa45c74570129448a42084" + "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/8fd93be538992d556aaa45c74570129448a42084", - "reference": "8fd93be538992d556aaa45c74570129448a42084", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/90f41072d220e5c40df6e8635f5dafba2d9d4d04", + "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04", "shasum": "" }, "require": { @@ -12005,7 +12005,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "4.1-dev" + "dev-main": "4.2-dev" } }, "autoload": { @@ -12029,7 +12029,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.1.0" + "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.2.0" }, "funding": [ { @@ -12049,7 +12049,7 @@ "type": "tidelift" } ], - "time": "2025-09-13T14:16:18+00:00" + "time": "2025-09-14T09:36:45+00:00" }, { "name": "sebastian/comparator", diff --git a/config/firefly.php b/config/firefly.php index eecaf07c61..9ebbd995ed 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -78,8 +78,8 @@ return [ 'running_balance_column' => env('USE_RUNNING_BALANCE', false), // see cer.php for exchange rates feature flag. ], - 'version' => 'develop/2025-09-13', - 'build_time' => 1757782204, + 'version' => 'develop/2025-09-15', + 'build_time' => 1757906521, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 26, diff --git a/package-lock.json b/package-lock.json index 211a5d750c..46e4e426d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3159,13 +3159,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.3.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.3.tgz", - "integrity": "sha512-GKBNHjoNw3Kra1Qg5UXttsY5kiWMEfoHq2TmXb+b1rcm6N7B3wTrFYIf/oSZ1xNQ+hVVijgLkiDZh7jRRsh+Gw==", + "version": "24.4.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.4.0.tgz", + "integrity": "sha512-gUuVEAK4/u6F9wRLznPUU4WGUacSEBDPoC2TrBkw3GAnOLHBL45QdfHOXp1kJ4ypBGLxTOB+t7NJLpKoC3gznQ==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.10.0" + "undici-types": "~7.11.0" } }, "node_modules/@types/node-forge": { @@ -3949,9 +3949,9 @@ } }, "node_modules/axios": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.1.tgz", - "integrity": "sha512-Kn4kbSXpkFHCGE6rBFNwIv0GQs4AvDT80jlveJDKFxjbTYMUeB4QtsdPCv6H8Cm19Je7IU6VFtRl2zWZI0rudQ==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", "dev": true, "license": "MIT", "dependencies": { @@ -5375,9 +5375,9 @@ "license": "MIT" }, "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { @@ -11357,9 +11357,9 @@ } }, "node_modules/undici-types": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", - "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.11.0.tgz", + "integrity": "sha512-kt1ZriHTi7MU+Z/r9DOdAI3ONdaR3M3csEaRc6ewa4f4dTvX4cQCbJ4NkEn0ohE4hHtq85+PhPSTY+pO/1PwgA==", "dev": true, "license": "MIT" }, From ecfb3e2f95048b1cbf8524dd90f9ab779760cf2e Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 15 Sep 2025 19:20:51 +0200 Subject: [PATCH 11/91] Fix #10854 and another issue (again). --- app/Http/Controllers/Controller.php | 3 +- .../Transaction/ShowController.php | 68 +++++++------------ .../TransactionGroupRepository.php | 5 +- app/Support/Amount.php | 60 ++++++++-------- app/Support/Twig/AmountFormat.php | 18 ++--- resources/views/transactions/show.twig | 4 +- 6 files changed, 72 insertions(+), 86 deletions(-) diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 7a4c542730..0919f8c199 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -26,6 +26,7 @@ namespace FireflyIII\Http\Controllers; use FireflyIII\Events\RequestedSendWebhookMessages; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Support\Facades\Amount; +use FireflyIII\Support\Facades\Preferences; use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Http\Controllers\RequestInformation; use FireflyIII\Support\Http\Controllers\UserNavigation; @@ -133,7 +134,7 @@ abstract class Controller extends BaseController $this->primaryCurrency = Amount::getPrimaryCurrency(); $language = Steam::getLanguage(); $locale = Steam::getLocale(); - $darkMode = app('preferences')->get('darkMode', 'browser')->data; + $darkMode = Preferences::get('darkMode', 'browser')->data; $this->convertToPrimary = Amount::convertToPrimary(); $page = $this->getPageName(); $shownDemo = $this->hasSeenDemo(); diff --git a/app/Http/Controllers/Transaction/ShowController.php b/app/Http/Controllers/Transaction/ShowController.php index 2d9e81f511..12627a55fe 100644 --- a/app/Http/Controllers/Transaction/ShowController.php +++ b/app/Http/Controllers/Transaction/ShowController.php @@ -85,52 +85,52 @@ class ShowController extends Controller public function show(TransactionGroup $transactionGroup) { /** @var User $admin */ - $admin = auth()->user(); + $admin = auth()->user(); // use new group collector: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setUser($admin)->setTransactionGroup($transactionGroup)->withAPIInformation(); /** @var null|TransactionGroup $selectedGroup */ - $selectedGroup = $collector->getGroups()->first(); + $selectedGroup = $collector->getGroups()->first(); if (null === $selectedGroup) { throw new NotFoundHttpException(); } // enrich - $enrichment = new TransactionGroupEnrichment(); + $enrichment = new TransactionGroupEnrichment(); $enrichment->setUser($admin); - $selectedGroup = $enrichment->enrichSingle($selectedGroup); + $selectedGroup = $enrichment->enrichSingle($selectedGroup); - $splits = count($selectedGroup['transactions']); - $keys = array_keys($selectedGroup['transactions']); - $first = $selectedGroup['transactions'][array_shift($keys)]; + $splits = count($selectedGroup['transactions']); + $keys = array_keys($selectedGroup['transactions']); + $first = $selectedGroup['transactions'][array_shift($keys)]; unset($keys); if (null === $first) { throw new FireflyException('This transaction is broken :(.'); } - $type = (string)trans(sprintf('firefly.%s', $first['transaction_type_type'])); - $title = 1 === $splits ? $first['description'] : $selectedGroup['title']; - $subTitle = sprintf('%s: "%s"', $type, $title); + $type = (string)trans(sprintf('firefly.%s', $first['transaction_type_type'])); + $title = 1 === $splits ? $first['description'] : $selectedGroup['title']; + $subTitle = sprintf('%s: "%s"', $type, $title); // enrich - $enrichment = new TransactionGroupEnrichment(); + $enrichment = new TransactionGroupEnrichment(); $enrichment->setUser($admin); /** @var array $selectedGroup */ - $selectedGroup = $enrichment->enrichSingle($selectedGroup); + $selectedGroup = $enrichment->enrichSingle($selectedGroup); /** @var TransactionGroupTransformer $transformer */ - $transformer = app(TransactionGroupTransformer::class); + $transformer = app(TransactionGroupTransformer::class); $transformer->setParameters(new ParameterBag()); - $groupArray = $transformer->transformObject($transactionGroup); + $groupArray = $transformer->transformObject($transactionGroup); // do some calculations: - $amounts = $this->getAmounts($selectedGroup); - $accounts = $this->getAccounts($selectedGroup); + $amounts = $this->getAmounts($selectedGroup); + $accounts = $this->getAccounts($selectedGroup); foreach (array_keys($selectedGroup['transactions']) as $index) { $selectedGroup['transactions'][$index]['tags'] = $this->repository->getTagObjects((int)$selectedGroup['transactions'][$index]['transaction_journal_id']); @@ -142,29 +142,11 @@ class ShowController extends Controller $logEntries[$journal['transaction_journal_id']] = $this->aleRepository->getForId(TransactionJournal::class, $journal['transaction_journal_id']); } - $events = $this->repository->getPiggyEvents($transactionGroup); - $attachments = $this->repository->getAttachments($transactionGroup); - $links = $this->repository->getLinks($transactionGroup); + $events = $this->repository->getPiggyEvents($transactionGroup); + $attachments = $this->repository->getAttachments($transactionGroup); + $links = $this->repository->getLinks($transactionGroup); - return view( - 'transactions.show', - compact( - 'transactionGroup', - 'amounts', - 'first', - 'type', - 'logEntries', - 'groupLogEntries', - 'subTitle', - 'splits', - 'selectedGroup', - 'groupArray', - 'events', - 'attachments', - 'links', - 'accounts', - ) - ); + return view('transactions.show', compact('transactionGroup', 'amounts', 'first', 'type', 'logEntries', 'groupLogEntries', 'subTitle', 'splits', 'selectedGroup', 'groupArray', 'events', 'attachments', 'links', 'accounts')); } private function getAmounts(array $group): array @@ -173,7 +155,7 @@ class ShowController extends Controller foreach ($group['transactions'] as $transaction) { // add normal amount: $symbol = $transaction['currency_symbol']; - $amounts[$symbol] ??= [ + $amounts[$symbol] ??= [ 'amount' => '0', 'symbol' => $symbol, 'decimal_places' => $transaction['currency_decimal_places'], @@ -184,7 +166,7 @@ class ShowController extends Controller if (null !== $transaction['foreign_amount'] && '' !== $transaction['foreign_amount'] && 0 !== bccomp('0', (string)$transaction['foreign_amount'])) { // same for foreign currency: $foreignSymbol = $transaction['foreign_currency_symbol']; - $amounts[$foreignSymbol] ??= [ + $amounts[$foreignSymbol] ??= [ 'amount' => '0', 'symbol' => $foreignSymbol, 'decimal_places' => $transaction['foreign_currency_decimal_places'], @@ -195,7 +177,7 @@ class ShowController extends Controller if (null !== $transaction['pc_amount'] && $transaction['currency_id'] !== $this->primaryCurrency->id) { // same for foreign currency: $primarySymbol = $this->primaryCurrency->symbol; - $amounts[$primarySymbol] ??= [ + $amounts[$primarySymbol] ??= [ 'amount' => '0', 'symbol' => $this->primaryCurrency->symbol, 'decimal_places' => $this->primaryCurrency->decimal_places, @@ -210,7 +192,7 @@ class ShowController extends Controller private function getAccounts(array $group): array { - $accounts = [ + $accounts = [ 'source' => [], 'destination' => [], ]; diff --git a/app/Repositories/TransactionGroup/TransactionGroupRepository.php b/app/Repositories/TransactionGroup/TransactionGroupRepository.php index fec5d3f42f..e658b8756d 100644 --- a/app/Repositories/TransactionGroup/TransactionGroupRepository.php +++ b/app/Repositories/TransactionGroup/TransactionGroupRepository.php @@ -370,8 +370,11 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface, public function getTagObjects(int $journalId): Collection { - /** @var TransactionJournal $journal */ + /** @var TransactionJournal|null $journal */ $journal = $this->user->transactionJournals()->find($journalId); + if(null ===$journal) { + return new Collection(); + } return $journal->tags()->whereNull('deleted_at')->get(); } diff --git a/app/Support/Amount.php b/app/Support/Amount.php index 547cf8dd79..53c067e015 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -60,15 +60,15 @@ class Amount */ public function formatFlat(string $symbol, int $decimalPlaces, string $amount, ?bool $coloured = null): string { - $locale = Steam::getLocale(); - $rounded = Steam::bcround($amount, $decimalPlaces); + $locale = Steam::getLocale(); + $rounded = Steam::bcround($amount, $decimalPlaces); $coloured ??= true; - $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY); + $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY); $fmt->setSymbol(NumberFormatter::CURRENCY_SYMBOL, $symbol); $fmt->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, $decimalPlaces); $fmt->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, $decimalPlaces); - $result = (string)$fmt->format((float)$rounded); // intentional float + $result = (string)$fmt->format((float)$rounded); // intentional float if (true === $coloured) { if (1 === bccomp($rounded, '0')) { @@ -122,13 +122,13 @@ class Amount $key = sprintf('transaction_currency_%d', $currencyId); /** @var null|TransactionCurrency $pref */ - $pref = $instance->getPreference($key); + $pref = $instance->getPreference($key); if (null !== $pref) { return $pref; } $currency = TransactionCurrency::find($currencyId); if (null === $currency) { - $message = sprintf('Could not find a transaction currency with ID #%d', $currencyId); + $message = sprintf('Could not find a transaction currency with ID #%d in %s', $currencyId, __METHOD__); Log::error($message); throw new FireflyException($message); @@ -144,13 +144,13 @@ class Amount $key = sprintf('transaction_currency_%s', $code); /** @var null|TransactionCurrency $pref */ - $pref = $instance->getPreference($key); + $pref = $instance->getPreference($key); if (null !== $pref) { return $pref; } $currency = TransactionCurrency::whereCode($code)->first(); if (null === $currency) { - $message = sprintf('Could not find a transaction currency with code "%s"', $code); + $message = sprintf('Could not find a transaction currency with code "%s" in %s', $code, __METHOD__); Log::error($message); throw new FireflyException($message); @@ -174,8 +174,8 @@ class Amount return $pref; } - $key = sprintf('convert_to_primary_%d', $user->id); - $pref = $instance->getPreference($key); + $key = sprintf('convert_to_primary_%d', $user->id); + $pref = $instance->getPreference($key); if (null === $pref) { $res = true === Preferences::getForUser($user, 'convert_to_primary', false)->data && true === config('cer.enabled'); $instance->setPreference($key, $res); @@ -201,7 +201,7 @@ class Amount public function getPrimaryCurrencyByUserGroup(UserGroup $userGroup): TransactionCurrency { - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty('getPrimaryCurrencyByGroup'); $cache->addProperty($userGroup->id); if ($cache->has()) { @@ -231,16 +231,16 @@ class Amount */ public function getAmountFromJournalObject(TransactionJournal $journal): string { - $convertToPrimary = $this->convertToPrimary(); - $currency = $this->getPrimaryCurrency(); - $field = $convertToPrimary && $currency->id !== $journal->transaction_currency_id ? 'pc_amount' : 'amount'; + $convertToPrimary = $this->convertToPrimary(); + $currency = $this->getPrimaryCurrency(); + $field = $convertToPrimary && $currency->id !== $journal->transaction_currency_id ? 'pc_amount' : 'amount'; /** @var null|Transaction $sourceTransaction */ $sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first(); if (null === $sourceTransaction) { return '0'; } - $amount = $sourceTransaction->{$field} ?? '0'; + $amount = $sourceTransaction->{$field} ?? '0'; if ((int)$sourceTransaction->foreign_currency_id === $currency->id) { // use foreign amount instead! $amount = (string)$sourceTransaction->foreign_amount; // hard coded to be foreign amount. @@ -288,20 +288,20 @@ class Amount private function getLocaleInfo(): array { // get config from preference, not from translation: - $locale = Steam::getLocale(); - $array = Steam::getLocaleArray($locale); + $locale = Steam::getLocale(); + $array = Steam::getLocaleArray($locale); setlocale(LC_MONETARY, $array); - $info = localeconv(); + $info = localeconv(); // correct variables - $info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes'); - $info['p_cs_precedes'] = $this->getLocaleField($info, 'p_cs_precedes'); + $info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes'); + $info['p_cs_precedes'] = $this->getLocaleField($info, 'p_cs_precedes'); - $info['n_sep_by_space'] = $this->getLocaleField($info, 'n_sep_by_space'); - $info['p_sep_by_space'] = $this->getLocaleField($info, 'p_sep_by_space'); + $info['n_sep_by_space'] = $this->getLocaleField($info, 'n_sep_by_space'); + $info['p_sep_by_space'] = $this->getLocaleField($info, 'p_sep_by_space'); - $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY); + $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY); $info['mon_decimal_point'] = $fmt->getSymbol(NumberFormatter::MONETARY_SEPARATOR_SYMBOL); $info['mon_thousands_sep'] = $fmt->getSymbol(NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL); @@ -333,11 +333,11 @@ class Amount // there are five possible positions for the "+" or "-" sign (if it is even used) // pos_a and pos_e could be the ( and ) symbol. - $posA = ''; // before everything - $posB = ''; // before currency symbol - $posC = ''; // after currency symbol - $posD = ''; // before amount - $posE = ''; // after everything + $posA = ''; // before everything + $posB = ''; // before currency symbol + $posC = ''; // after currency symbol + $posD = ''; // before amount + $posE = ''; // after everything // format would be (currency before amount) // AB%sC_D%vE @@ -379,9 +379,9 @@ class Amount } if ($csPrecedes) { - return $posA.$posB.'%s'.$posC.$space.$posD.'%v'.$posE; + return $posA . $posB . '%s' . $posC . $space . $posD . '%v' . $posE; } - return $posA.$posD.'%v'.$space.$posB.'%s'.$posC.$posE; + return $posA . $posD . '%v' . $space . $posB . '%s' . $posC . $posE; } } diff --git a/app/Support/Twig/AmountFormat.php b/app/Support/Twig/AmountFormat.php index ac6efaa8b7..20402e0217 100644 --- a/app/Support/Twig/AmountFormat.php +++ b/app/Support/Twig/AmountFormat.php @@ -114,20 +114,20 @@ class AmountFormat extends AbstractExtension { return new TwigFunction( 'formatAmountBySymbol', - static function (string $amount, ?string $symbol, ?int $decimalPlaces = null, ?bool $coloured = null): string { + static function (string $amount, ?string $symbol = null, ?int $decimalPlaces = null, ?bool $coloured = null): string { if (null === $symbol) { $message = sprintf('formatAmountBySymbol("%s", %s, %d, %s) was called without a symbol. Please browse to /flush to clear your cache.', $amount, var_export($symbol, true), $decimalPlaces, var_export($coloured, true)); Log::error($message); - - throw new FireflyException($message); + $currency = Amount::getPrimaryCurrency(); + } + if (null !== $symbol) { + $decimalPlaces ??= 2; + $coloured ??= true; + $currency = new TransactionCurrency(); + $currency->symbol = $symbol; + $currency->decimal_places = $decimalPlaces; } - - $decimalPlaces ??= 2; - $coloured ??= true; - $currency = new TransactionCurrency(); - $currency->symbol = $symbol; - $currency->decimal_places = $decimalPlaces; return Amount::formatAnything($currency, $amount, $coloured); }, diff --git a/resources/views/transactions/show.twig b/resources/views/transactions/show.twig index 9202da88dd..1b9463fc83 100644 --- a/resources/views/transactions/show.twig +++ b/resources/views/transactions/show.twig @@ -203,7 +203,7 @@ {% set boxSize = 4 %} {% endif %}
- {% for index,journal in selectedGroup.transactions %} + {% for index, journal in selectedGroup.transactions %}
@@ -440,7 +440,7 @@ {{ 'tags'|_ }} {% for tag in journal.tags %} - {% if null != tag.id %} + {% if null != tag.id and '' != tag.id %}

{{ tag.tag }}

{% endif %} {% endfor %} From 53335077ff619d65cececfffcaf40f11353e34ae Mon Sep 17 00:00:00 2001 From: JC5 Date: Mon, 15 Sep 2025 19:25:59 +0200 Subject: [PATCH 12/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-09-15?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Transaction/ShowController.php | 48 ++--- .../TransactionGroupRepository.php | 4 +- app/Support/Amount.php | 56 ++--- app/Support/Twig/AmountFormat.php | 6 +- config/firefly.php | 2 +- package-lock.json | 194 +++++++++--------- 6 files changed, 155 insertions(+), 155 deletions(-) diff --git a/app/Http/Controllers/Transaction/ShowController.php b/app/Http/Controllers/Transaction/ShowController.php index 12627a55fe..7c2fff6105 100644 --- a/app/Http/Controllers/Transaction/ShowController.php +++ b/app/Http/Controllers/Transaction/ShowController.php @@ -85,52 +85,52 @@ class ShowController extends Controller public function show(TransactionGroup $transactionGroup) { /** @var User $admin */ - $admin = auth()->user(); + $admin = auth()->user(); // use new group collector: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setUser($admin)->setTransactionGroup($transactionGroup)->withAPIInformation(); /** @var null|TransactionGroup $selectedGroup */ - $selectedGroup = $collector->getGroups()->first(); + $selectedGroup = $collector->getGroups()->first(); if (null === $selectedGroup) { throw new NotFoundHttpException(); } // enrich - $enrichment = new TransactionGroupEnrichment(); + $enrichment = new TransactionGroupEnrichment(); $enrichment->setUser($admin); - $selectedGroup = $enrichment->enrichSingle($selectedGroup); + $selectedGroup = $enrichment->enrichSingle($selectedGroup); - $splits = count($selectedGroup['transactions']); - $keys = array_keys($selectedGroup['transactions']); - $first = $selectedGroup['transactions'][array_shift($keys)]; + $splits = count($selectedGroup['transactions']); + $keys = array_keys($selectedGroup['transactions']); + $first = $selectedGroup['transactions'][array_shift($keys)]; unset($keys); if (null === $first) { throw new FireflyException('This transaction is broken :(.'); } - $type = (string)trans(sprintf('firefly.%s', $first['transaction_type_type'])); - $title = 1 === $splits ? $first['description'] : $selectedGroup['title']; - $subTitle = sprintf('%s: "%s"', $type, $title); + $type = (string)trans(sprintf('firefly.%s', $first['transaction_type_type'])); + $title = 1 === $splits ? $first['description'] : $selectedGroup['title']; + $subTitle = sprintf('%s: "%s"', $type, $title); // enrich - $enrichment = new TransactionGroupEnrichment(); + $enrichment = new TransactionGroupEnrichment(); $enrichment->setUser($admin); /** @var array $selectedGroup */ - $selectedGroup = $enrichment->enrichSingle($selectedGroup); + $selectedGroup = $enrichment->enrichSingle($selectedGroup); /** @var TransactionGroupTransformer $transformer */ - $transformer = app(TransactionGroupTransformer::class); + $transformer = app(TransactionGroupTransformer::class); $transformer->setParameters(new ParameterBag()); - $groupArray = $transformer->transformObject($transactionGroup); + $groupArray = $transformer->transformObject($transactionGroup); // do some calculations: - $amounts = $this->getAmounts($selectedGroup); - $accounts = $this->getAccounts($selectedGroup); + $amounts = $this->getAmounts($selectedGroup); + $accounts = $this->getAccounts($selectedGroup); foreach (array_keys($selectedGroup['transactions']) as $index) { $selectedGroup['transactions'][$index]['tags'] = $this->repository->getTagObjects((int)$selectedGroup['transactions'][$index]['transaction_journal_id']); @@ -142,9 +142,9 @@ class ShowController extends Controller $logEntries[$journal['transaction_journal_id']] = $this->aleRepository->getForId(TransactionJournal::class, $journal['transaction_journal_id']); } - $events = $this->repository->getPiggyEvents($transactionGroup); - $attachments = $this->repository->getAttachments($transactionGroup); - $links = $this->repository->getLinks($transactionGroup); + $events = $this->repository->getPiggyEvents($transactionGroup); + $attachments = $this->repository->getAttachments($transactionGroup); + $links = $this->repository->getLinks($transactionGroup); return view('transactions.show', compact('transactionGroup', 'amounts', 'first', 'type', 'logEntries', 'groupLogEntries', 'subTitle', 'splits', 'selectedGroup', 'groupArray', 'events', 'attachments', 'links', 'accounts')); } @@ -155,7 +155,7 @@ class ShowController extends Controller foreach ($group['transactions'] as $transaction) { // add normal amount: $symbol = $transaction['currency_symbol']; - $amounts[$symbol] ??= [ + $amounts[$symbol] ??= [ 'amount' => '0', 'symbol' => $symbol, 'decimal_places' => $transaction['currency_decimal_places'], @@ -166,7 +166,7 @@ class ShowController extends Controller if (null !== $transaction['foreign_amount'] && '' !== $transaction['foreign_amount'] && 0 !== bccomp('0', (string)$transaction['foreign_amount'])) { // same for foreign currency: $foreignSymbol = $transaction['foreign_currency_symbol']; - $amounts[$foreignSymbol] ??= [ + $amounts[$foreignSymbol] ??= [ 'amount' => '0', 'symbol' => $foreignSymbol, 'decimal_places' => $transaction['foreign_currency_decimal_places'], @@ -177,7 +177,7 @@ class ShowController extends Controller if (null !== $transaction['pc_amount'] && $transaction['currency_id'] !== $this->primaryCurrency->id) { // same for foreign currency: $primarySymbol = $this->primaryCurrency->symbol; - $amounts[$primarySymbol] ??= [ + $amounts[$primarySymbol] ??= [ 'amount' => '0', 'symbol' => $this->primaryCurrency->symbol, 'decimal_places' => $this->primaryCurrency->decimal_places, @@ -192,7 +192,7 @@ class ShowController extends Controller private function getAccounts(array $group): array { - $accounts = [ + $accounts = [ 'source' => [], 'destination' => [], ]; diff --git a/app/Repositories/TransactionGroup/TransactionGroupRepository.php b/app/Repositories/TransactionGroup/TransactionGroupRepository.php index e658b8756d..c8c03b7a38 100644 --- a/app/Repositories/TransactionGroup/TransactionGroupRepository.php +++ b/app/Repositories/TransactionGroup/TransactionGroupRepository.php @@ -370,9 +370,9 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface, public function getTagObjects(int $journalId): Collection { - /** @var TransactionJournal|null $journal */ + /** @var null|TransactionJournal $journal */ $journal = $this->user->transactionJournals()->find($journalId); - if(null ===$journal) { + if (null === $journal) { return new Collection(); } diff --git a/app/Support/Amount.php b/app/Support/Amount.php index 53c067e015..c3d82f0caf 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -60,15 +60,15 @@ class Amount */ public function formatFlat(string $symbol, int $decimalPlaces, string $amount, ?bool $coloured = null): string { - $locale = Steam::getLocale(); - $rounded = Steam::bcround($amount, $decimalPlaces); + $locale = Steam::getLocale(); + $rounded = Steam::bcround($amount, $decimalPlaces); $coloured ??= true; - $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY); + $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY); $fmt->setSymbol(NumberFormatter::CURRENCY_SYMBOL, $symbol); $fmt->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, $decimalPlaces); $fmt->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, $decimalPlaces); - $result = (string)$fmt->format((float)$rounded); // intentional float + $result = (string)$fmt->format((float)$rounded); // intentional float if (true === $coloured) { if (1 === bccomp($rounded, '0')) { @@ -122,7 +122,7 @@ class Amount $key = sprintf('transaction_currency_%d', $currencyId); /** @var null|TransactionCurrency $pref */ - $pref = $instance->getPreference($key); + $pref = $instance->getPreference($key); if (null !== $pref) { return $pref; } @@ -144,7 +144,7 @@ class Amount $key = sprintf('transaction_currency_%s', $code); /** @var null|TransactionCurrency $pref */ - $pref = $instance->getPreference($key); + $pref = $instance->getPreference($key); if (null !== $pref) { return $pref; } @@ -174,8 +174,8 @@ class Amount return $pref; } - $key = sprintf('convert_to_primary_%d', $user->id); - $pref = $instance->getPreference($key); + $key = sprintf('convert_to_primary_%d', $user->id); + $pref = $instance->getPreference($key); if (null === $pref) { $res = true === Preferences::getForUser($user, 'convert_to_primary', false)->data && true === config('cer.enabled'); $instance->setPreference($key, $res); @@ -201,7 +201,7 @@ class Amount public function getPrimaryCurrencyByUserGroup(UserGroup $userGroup): TransactionCurrency { - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty('getPrimaryCurrencyByGroup'); $cache->addProperty($userGroup->id); if ($cache->has()) { @@ -231,16 +231,16 @@ class Amount */ public function getAmountFromJournalObject(TransactionJournal $journal): string { - $convertToPrimary = $this->convertToPrimary(); - $currency = $this->getPrimaryCurrency(); - $field = $convertToPrimary && $currency->id !== $journal->transaction_currency_id ? 'pc_amount' : 'amount'; + $convertToPrimary = $this->convertToPrimary(); + $currency = $this->getPrimaryCurrency(); + $field = $convertToPrimary && $currency->id !== $journal->transaction_currency_id ? 'pc_amount' : 'amount'; /** @var null|Transaction $sourceTransaction */ $sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first(); if (null === $sourceTransaction) { return '0'; } - $amount = $sourceTransaction->{$field} ?? '0'; + $amount = $sourceTransaction->{$field} ?? '0'; if ((int)$sourceTransaction->foreign_currency_id === $currency->id) { // use foreign amount instead! $amount = (string)$sourceTransaction->foreign_amount; // hard coded to be foreign amount. @@ -288,20 +288,20 @@ class Amount private function getLocaleInfo(): array { // get config from preference, not from translation: - $locale = Steam::getLocale(); - $array = Steam::getLocaleArray($locale); + $locale = Steam::getLocale(); + $array = Steam::getLocaleArray($locale); setlocale(LC_MONETARY, $array); - $info = localeconv(); + $info = localeconv(); // correct variables - $info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes'); - $info['p_cs_precedes'] = $this->getLocaleField($info, 'p_cs_precedes'); + $info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes'); + $info['p_cs_precedes'] = $this->getLocaleField($info, 'p_cs_precedes'); - $info['n_sep_by_space'] = $this->getLocaleField($info, 'n_sep_by_space'); - $info['p_sep_by_space'] = $this->getLocaleField($info, 'p_sep_by_space'); + $info['n_sep_by_space'] = $this->getLocaleField($info, 'n_sep_by_space'); + $info['p_sep_by_space'] = $this->getLocaleField($info, 'p_sep_by_space'); - $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY); + $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY); $info['mon_decimal_point'] = $fmt->getSymbol(NumberFormatter::MONETARY_SEPARATOR_SYMBOL); $info['mon_thousands_sep'] = $fmt->getSymbol(NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL); @@ -333,11 +333,11 @@ class Amount // there are five possible positions for the "+" or "-" sign (if it is even used) // pos_a and pos_e could be the ( and ) symbol. - $posA = ''; // before everything - $posB = ''; // before currency symbol - $posC = ''; // after currency symbol - $posD = ''; // before amount - $posE = ''; // after everything + $posA = ''; // before everything + $posB = ''; // before currency symbol + $posC = ''; // after currency symbol + $posD = ''; // before amount + $posE = ''; // after everything // format would be (currency before amount) // AB%sC_D%vE @@ -379,9 +379,9 @@ class Amount } if ($csPrecedes) { - return $posA . $posB . '%s' . $posC . $space . $posD . '%v' . $posE; + return $posA.$posB.'%s'.$posC.$space.$posD.'%v'.$posE; } - return $posA . $posD . '%v' . $space . $posB . '%s' . $posC . $posE; + return $posA.$posD.'%v'.$space.$posB.'%s'.$posC.$posE; } } diff --git a/app/Support/Twig/AmountFormat.php b/app/Support/Twig/AmountFormat.php index 20402e0217..39cc1594db 100644 --- a/app/Support/Twig/AmountFormat.php +++ b/app/Support/Twig/AmountFormat.php @@ -117,13 +117,13 @@ class AmountFormat extends AbstractExtension static function (string $amount, ?string $symbol = null, ?int $decimalPlaces = null, ?bool $coloured = null): string { if (null === $symbol) { - $message = sprintf('formatAmountBySymbol("%s", %s, %d, %s) was called without a symbol. Please browse to /flush to clear your cache.', $amount, var_export($symbol, true), $decimalPlaces, var_export($coloured, true)); + $message = sprintf('formatAmountBySymbol("%s", %s, %d, %s) was called without a symbol. Please browse to /flush to clear your cache.', $amount, var_export($symbol, true), $decimalPlaces, var_export($coloured, true)); Log::error($message); $currency = Amount::getPrimaryCurrency(); } if (null !== $symbol) { - $decimalPlaces ??= 2; - $coloured ??= true; + $decimalPlaces ??= 2; + $coloured ??= true; $currency = new TransactionCurrency(); $currency->symbol = $symbol; $currency->decimal_places = $decimalPlaces; diff --git a/config/firefly.php b/config/firefly.php index 9ebbd995ed..7fa9eff12a 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -79,7 +79,7 @@ return [ // see cer.php for exchange rates feature flag. ], 'version' => 'develop/2025-09-15', - 'build_time' => 1757906521, + 'build_time' => 1757957039, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 26, diff --git a/package-lock.json b/package-lock.json index 46e4e426d6..b8d603f07b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2589,9 +2589,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.1.tgz", - "integrity": "sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.2.tgz", + "integrity": "sha512-uLN8NAiFVIRKX9ZQha8wy6UUs06UNSZ32xj6giK/rmMXAgKahwExvK6SsmgU5/brh4w/nSgj8e0k3c1HBQpa0A==", "cpu": [ "arm" ], @@ -2603,9 +2603,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.1.tgz", - "integrity": "sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.2.tgz", + "integrity": "sha512-oEouqQk2/zxxj22PNcGSskya+3kV0ZKH+nQxuCCOGJ4oTXBdNTbv+f/E3c74cNLeMO1S5wVWacSws10TTSB77g==", "cpu": [ "arm64" ], @@ -2617,9 +2617,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.1.tgz", - "integrity": "sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.2.tgz", + "integrity": "sha512-OZuTVTpj3CDSIxmPgGH8en/XtirV5nfljHZ3wrNwvgkT5DQLhIKAeuFSiwtbMto6oVexV0k1F1zqURPKf5rI1Q==", "cpu": [ "arm64" ], @@ -2631,9 +2631,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.1.tgz", - "integrity": "sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.2.tgz", + "integrity": "sha512-Wa/Wn8RFkIkr1vy1k1PB//VYhLnlnn5eaJkfTQKivirOvzu5uVd2It01ukeQstMursuz7S1bU+8WW+1UPXpa8A==", "cpu": [ "x64" ], @@ -2645,9 +2645,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.1.tgz", - "integrity": "sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.2.tgz", + "integrity": "sha512-QkzxvH3kYN9J1w7D1A+yIMdI1pPekD+pWx7G5rXgnIlQ1TVYVC6hLl7SOV9pi5q9uIDF9AuIGkuzcbF7+fAhow==", "cpu": [ "arm64" ], @@ -2659,9 +2659,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.1.tgz", - "integrity": "sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.2.tgz", + "integrity": "sha512-dkYXB0c2XAS3a3jmyDkX4Jk0m7gWLFzq1C3qUnJJ38AyxIF5G/dyS4N9B30nvFseCfgtCEdbYFhk0ChoCGxPog==", "cpu": [ "x64" ], @@ -2673,9 +2673,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.1.tgz", - "integrity": "sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.2.tgz", + "integrity": "sha512-9VlPY/BN3AgbukfVHAB8zNFWB/lKEuvzRo1NKev0Po8sYFKx0i+AQlCYftgEjcL43F2h9Ui1ZSdVBc4En/sP2w==", "cpu": [ "arm" ], @@ -2687,9 +2687,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.1.tgz", - "integrity": "sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.2.tgz", + "integrity": "sha512-+GdKWOvsifaYNlIVf07QYan1J5F141+vGm5/Y8b9uCZnG/nxoGqgCmR24mv0koIWWuqvFYnbURRqw1lv7IBINw==", "cpu": [ "arm" ], @@ -2701,9 +2701,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.1.tgz", - "integrity": "sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.2.tgz", + "integrity": "sha512-df0Eou14ojtUdLQdPFnymEQteENwSJAdLf5KCDrmZNsy1c3YaCNaJvYsEUHnrg+/DLBH612/R0xd3dD03uz2dg==", "cpu": [ "arm64" ], @@ -2715,9 +2715,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.1.tgz", - "integrity": "sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.2.tgz", + "integrity": "sha512-iPeouV0UIDtz8j1YFR4OJ/zf7evjauqv7jQ/EFs0ClIyL+by++hiaDAfFipjOgyz6y6xbDvJuiU4HwpVMpRFDQ==", "cpu": [ "arm64" ], @@ -2728,10 +2728,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.50.1.tgz", - "integrity": "sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.50.2.tgz", + "integrity": "sha512-OL6KaNvBopLlj5fTa5D5bau4W82f+1TyTZRr2BdnfsrnQnmdxh4okMxR2DcDkJuh4KeoQZVuvHvzuD/lyLn2Kw==", "cpu": [ "loong64" ], @@ -2743,9 +2743,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.1.tgz", - "integrity": "sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.2.tgz", + "integrity": "sha512-I21VJl1w6z/K5OTRl6aS9DDsqezEZ/yKpbqlvfHbW0CEF5IL8ATBMuUx6/mp683rKTK8thjs/0BaNrZLXetLag==", "cpu": [ "ppc64" ], @@ -2757,9 +2757,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.1.tgz", - "integrity": "sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.2.tgz", + "integrity": "sha512-Hq6aQJT/qFFHrYMjS20nV+9SKrXL2lvFBENZoKfoTH2kKDOJqff5OSJr4x72ZaG/uUn+XmBnGhfr4lwMRrmqCQ==", "cpu": [ "riscv64" ], @@ -2771,9 +2771,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.1.tgz", - "integrity": "sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.2.tgz", + "integrity": "sha512-82rBSEXRv5qtKyr0xZ/YMF531oj2AIpLZkeNYxmKNN6I2sVE9PGegN99tYDLK2fYHJITL1P2Lgb4ZXnv0PjQvw==", "cpu": [ "riscv64" ], @@ -2785,9 +2785,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.1.tgz", - "integrity": "sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.2.tgz", + "integrity": "sha512-4Q3S3Hy7pC6uaRo9gtXUTJ+EKo9AKs3BXKc2jYypEcMQ49gDPFU2P1ariX9SEtBzE5egIX6fSUmbmGazwBVF9w==", "cpu": [ "s390x" ], @@ -2799,9 +2799,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.1.tgz", - "integrity": "sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.2.tgz", + "integrity": "sha512-9Jie/At6qk70dNIcopcL4p+1UirusEtznpNtcq/u/C5cC4HBX7qSGsYIcG6bdxj15EYWhHiu02YvmdPzylIZlA==", "cpu": [ "x64" ], @@ -2813,9 +2813,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.1.tgz", - "integrity": "sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.2.tgz", + "integrity": "sha512-HPNJwxPL3EmhzeAnsWQCM3DcoqOz3/IC6de9rWfGR8ZCuEHETi9km66bH/wG3YH0V3nyzyFEGUZeL5PKyy4xvw==", "cpu": [ "x64" ], @@ -2827,9 +2827,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.1.tgz", - "integrity": "sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.2.tgz", + "integrity": "sha512-nMKvq6FRHSzYfKLHZ+cChowlEkR2lj/V0jYj9JnGUVPL2/mIeFGmVM2mLaFeNa5Jev7W7TovXqXIG2d39y1KYA==", "cpu": [ "arm64" ], @@ -2841,9 +2841,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.1.tgz", - "integrity": "sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.2.tgz", + "integrity": "sha512-eFUvvnTYEKeTyHEijQKz81bLrUQOXKZqECeiWH6tb8eXXbZk+CXSG2aFrig2BQ/pjiVRj36zysjgILkqarS2YA==", "cpu": [ "arm64" ], @@ -2855,9 +2855,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.1.tgz", - "integrity": "sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.2.tgz", + "integrity": "sha512-cBaWmXqyfRhH8zmUxK3d3sAhEWLrtMjWBRwdMMHJIXSjvjLKvv49adxiEz+FJ8AP90apSDDBx2Tyd/WylV6ikA==", "cpu": [ "ia32" ], @@ -2869,9 +2869,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.1.tgz", - "integrity": "sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.2.tgz", + "integrity": "sha512-APwKy6YUhvZaEoHyM+9xqmTpviEI+9eL7LoCH+aLcvWYHJ663qG5zx7WzWZY+a9qkg5JtzcMyJ9z0WtQBMDmgA==", "cpu": [ "x64" ], @@ -4061,9 +4061,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.3.tgz", - "integrity": "sha512-mcE+Wr2CAhHNWxXN/DdTI+n4gsPc5QpXpWnyCQWiQYIYZX+ZMJ8juXZgjRa/0/YPJo/NSsgW15/YgmI4nbysYw==", + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.4.tgz", + "integrity": "sha512-L+YvJwGAgwJBV1p6ffpSTa2KRc69EeeYGYjRVWKs0GKrK+LON0GC0gV+rKSNtALEDvMDqkvCFq9r1r94/Gjwxw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5819,9 +5819,9 @@ } }, "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10145,9 +10145,9 @@ } }, "node_modules/rollup": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.1.tgz", - "integrity": "sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.2.tgz", + "integrity": "sha512-BgLRGy7tNS9H66aIMASq1qSYbAAJV6Z6WR4QYTvj5FgF15rZ/ympT1uixHXwzbZUBDbkvqUI1KR0fH1FhMaQ9w==", "dev": true, "license": "MIT", "dependencies": { @@ -10161,27 +10161,27 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.50.1", - "@rollup/rollup-android-arm64": "4.50.1", - "@rollup/rollup-darwin-arm64": "4.50.1", - "@rollup/rollup-darwin-x64": "4.50.1", - "@rollup/rollup-freebsd-arm64": "4.50.1", - "@rollup/rollup-freebsd-x64": "4.50.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.50.1", - "@rollup/rollup-linux-arm-musleabihf": "4.50.1", - "@rollup/rollup-linux-arm64-gnu": "4.50.1", - "@rollup/rollup-linux-arm64-musl": "4.50.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.50.1", - "@rollup/rollup-linux-ppc64-gnu": "4.50.1", - "@rollup/rollup-linux-riscv64-gnu": "4.50.1", - "@rollup/rollup-linux-riscv64-musl": "4.50.1", - "@rollup/rollup-linux-s390x-gnu": "4.50.1", - "@rollup/rollup-linux-x64-gnu": "4.50.1", - "@rollup/rollup-linux-x64-musl": "4.50.1", - "@rollup/rollup-openharmony-arm64": "4.50.1", - "@rollup/rollup-win32-arm64-msvc": "4.50.1", - "@rollup/rollup-win32-ia32-msvc": "4.50.1", - "@rollup/rollup-win32-x64-msvc": "4.50.1", + "@rollup/rollup-android-arm-eabi": "4.50.2", + "@rollup/rollup-android-arm64": "4.50.2", + "@rollup/rollup-darwin-arm64": "4.50.2", + "@rollup/rollup-darwin-x64": "4.50.2", + "@rollup/rollup-freebsd-arm64": "4.50.2", + "@rollup/rollup-freebsd-x64": "4.50.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.50.2", + "@rollup/rollup-linux-arm-musleabihf": "4.50.2", + "@rollup/rollup-linux-arm64-gnu": "4.50.2", + "@rollup/rollup-linux-arm64-musl": "4.50.2", + "@rollup/rollup-linux-loong64-gnu": "4.50.2", + "@rollup/rollup-linux-ppc64-gnu": "4.50.2", + "@rollup/rollup-linux-riscv64-gnu": "4.50.2", + "@rollup/rollup-linux-riscv64-musl": "4.50.2", + "@rollup/rollup-linux-s390x-gnu": "4.50.2", + "@rollup/rollup-linux-x64-gnu": "4.50.2", + "@rollup/rollup-linux-x64-musl": "4.50.2", + "@rollup/rollup-openharmony-arm64": "4.50.2", + "@rollup/rollup-win32-arm64-msvc": "4.50.2", + "@rollup/rollup-win32-ia32-msvc": "4.50.2", + "@rollup/rollup-win32-x64-msvc": "4.50.2", "fsevents": "~2.3.2" } }, @@ -11398,9 +11398,9 @@ } }, "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", + "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", "dev": true, "license": "MIT", "engines": { From b653d63d3d445276f0f48bceeec273fd63e148f5 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 16 Sep 2025 20:44:54 +0200 Subject: [PATCH 13/91] Add new correction command, will probably fix #10833 --- .../Commands/Correction/CorrectsDatabase.php | 3 +- .../RemovesLinksToDeletedObjects.php | 97 +++++++++++++++++++ .../2024_11_30_075826_multi_piggy.php | 2 +- 3 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 app/Console/Commands/Correction/RemovesLinksToDeletedObjects.php diff --git a/app/Console/Commands/Correction/CorrectsDatabase.php b/app/Console/Commands/Correction/CorrectsDatabase.php index 5850619f61..4288a4af40 100644 --- a/app/Console/Commands/Correction/CorrectsDatabase.php +++ b/app/Console/Commands/Correction/CorrectsDatabase.php @@ -75,7 +75,8 @@ class CorrectsDatabase extends Command 'correction:recalculates-liabilities', 'correction:preferences', // 'correction:transaction-types', // resource heavy, disabled. - 'correction:recalculate-pc-amounts', // not necessary, disabled. + 'correction:recalculate-pc-amounts', + 'correction:remove-links-to-deleted-objects', 'firefly-iii:report-integrity', ]; foreach ($commands as $command) { diff --git a/app/Console/Commands/Correction/RemovesLinksToDeletedObjects.php b/app/Console/Commands/Correction/RemovesLinksToDeletedObjects.php new file mode 100644 index 0000000000..56f0c737c9 --- /dev/null +++ b/app/Console/Commands/Correction/RemovesLinksToDeletedObjects.php @@ -0,0 +1,97 @@ +. + */ + +namespace FireflyIII\Console\Commands\Correction; + +use FireflyIII\Console\Commands\ShowsFriendlyMessages; +use FireflyIII\Models\Budget; +use FireflyIII\Models\Category; +use FireflyIII\Models\TransactionJournal; +use Illuminate\Console\Command; +use Illuminate\Support\Facades\DB; + +class RemovesLinksToDeletedObjects extends Command +{ + use ShowsFriendlyMessages; + + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'correction:remove-links-to-deleted-objects'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Removes deleted entries from intermediate tables.'; + + /** + * Execute the console command. + */ + public function handle() + { + // accounts + // remove soft-deleted accounts from account_balances + // remove soft-deleted accounts from account_meta + // remove soft-deleted accounts from account_piggy_bank + // remove soft-deleted accounts from attachments. + + // journals + // remove soft-deleted journals from attachments + // audit_log_entries + $deleted = TransactionJournal::withTrashed()->whereNotNull('deleted_at')->get('transaction_journals.id')->pluck('id')->toArray(); + $count = DB::table('tag_transaction_journal')->whereIn('transaction_journal_id', $deleted)->delete(); + if ($count > 0) { + $this->friendlyInfo(sprintf('Removed %d old relationship(s) between tags and transactions.', $count)); + } + unset($deleted); + // budgets + // from auto_budgets + // from budget_limits + $deleted = Budget::withTrashed()->whereNotNull('deleted_at')->get('budgets.id')->pluck('id')->toArray(); + $count = DB::table('budget_transaction')->whereIn('budget_id', $deleted)->delete(); + if ($count > 0) { + $this->friendlyInfo(sprintf('Removed %d old relationship(s) between budgets and transactions.', $count)); + } + $count = DB::table('budget_transaction_journal')->whereIn('budget_id', $deleted)->delete(); + if ($count > 0) { + $this->friendlyInfo(sprintf('Removed %d old relationship(s) between budgets and transactions.', $count)); + } + unset($deleted); + // -> category_transaction + // -> category_transaction_journal + $deleted = Category::withTrashed()->whereNotNull('deleted_at')->get('categories.id')->pluck('id')->toArray(); + $count = DB::table('category_transaction')->whereIn('category_id', $deleted)->delete(); + if ($count > 0) { + $this->friendlyInfo(sprintf('Removed %d old relationship(s) between categories and transactions.', $count)); + } + $count = DB::table('category_transaction_journal')->whereIn('category_id', $deleted)->delete(); + if ($count > 0) { + $this->friendlyInfo(sprintf('Removed %d old relationship(s) categories budgets and transactions.', $count)); + } + $this->friendlyNeutral('Validated links to deleted objects.'); + + + } +} diff --git a/database/migrations/2024_11_30_075826_multi_piggy.php b/database/migrations/2024_11_30_075826_multi_piggy.php index 19442cd051..a89f29440d 100644 --- a/database/migrations/2024_11_30_075826_multi_piggy.php +++ b/database/migrations/2024_11_30_075826_multi_piggy.php @@ -140,7 +140,7 @@ return new class () extends Migration { $table->dropColumn('transaction_currency_id'); // 2. make column non-nullable. - $table->unsignedInteger('account_id')->change(); + $table->unsignedInteger('account_id')->nullable()->change(); // 5. add new index $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); From 090aecb5f55646cbc9b154fdfccda548cb98e618 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 16 Sep 2025 20:50:25 +0200 Subject: [PATCH 14/91] Clean up command. --- .../RemovesLinksToDeletedObjects.php | 87 +++++++++++-------- 1 file changed, 53 insertions(+), 34 deletions(-) diff --git a/app/Console/Commands/Correction/RemovesLinksToDeletedObjects.php b/app/Console/Commands/Correction/RemovesLinksToDeletedObjects.php index 56f0c737c9..ea79dfa5e1 100644 --- a/app/Console/Commands/Correction/RemovesLinksToDeletedObjects.php +++ b/app/Console/Commands/Correction/RemovesLinksToDeletedObjects.php @@ -24,6 +24,7 @@ namespace FireflyIII\Console\Commands\Correction; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Models\Budget; use FireflyIII\Models\Category; +use FireflyIII\Models\Tag; use FireflyIII\Models\TransactionJournal; use Illuminate\Console\Command; use Illuminate\Support\Facades\DB; @@ -51,47 +52,65 @@ class RemovesLinksToDeletedObjects extends Command */ public function handle() { - // accounts - // remove soft-deleted accounts from account_balances - // remove soft-deleted accounts from account_meta - // remove soft-deleted accounts from account_piggy_bank - // remove soft-deleted accounts from attachments. + $deletedTags = Tag::withTrashed()->whereNotNull('deleted_at')->get('tags.id')->pluck('id')->toArray(); + $deletedJournals = TransactionJournal::withTrashed()->whereNotNull('deleted_at')->get('transaction_journals.id')->pluck('id')->toArray(); + $deletedBudgets = Budget::withTrashed()->whereNotNull('deleted_at')->get('budgets.id')->pluck('id')->toArray(); + $deletedCategories = Category::withTrashed()->whereNotNull('deleted_at')->get('categories.id')->pluck('id')->toArray(); - // journals - // remove soft-deleted journals from attachments - // audit_log_entries - $deleted = TransactionJournal::withTrashed()->whereNotNull('deleted_at')->get('transaction_journals.id')->pluck('id')->toArray(); - $count = DB::table('tag_transaction_journal')->whereIn('transaction_journal_id', $deleted)->delete(); - if ($count > 0) { - $this->friendlyInfo(sprintf('Removed %d old relationship(s) between tags and transactions.', $count)); + if (count($deletedTags) > 0) { + $this->cleanupTags($deletedTags); } - unset($deleted); - // budgets - // from auto_budgets - // from budget_limits - $deleted = Budget::withTrashed()->whereNotNull('deleted_at')->get('budgets.id')->pluck('id')->toArray(); - $count = DB::table('budget_transaction')->whereIn('budget_id', $deleted)->delete(); - if ($count > 0) { - $this->friendlyInfo(sprintf('Removed %d old relationship(s) between budgets and transactions.', $count)); + if (count($deletedJournals) > 0) { + $this->cleanupJournals($deletedJournals); } - $count = DB::table('budget_transaction_journal')->whereIn('budget_id', $deleted)->delete(); - if ($count > 0) { - $this->friendlyInfo(sprintf('Removed %d old relationship(s) between budgets and transactions.', $count)); + if (count($deletedBudgets) > 0) { + $this->cleanupBudgets($deletedBudgets); } - unset($deleted); - // -> category_transaction - // -> category_transaction_journal - $deleted = Category::withTrashed()->whereNotNull('deleted_at')->get('categories.id')->pluck('id')->toArray(); - $count = DB::table('category_transaction')->whereIn('category_id', $deleted)->delete(); - if ($count > 0) { - $this->friendlyInfo(sprintf('Removed %d old relationship(s) between categories and transactions.', $count)); - } - $count = DB::table('category_transaction_journal')->whereIn('category_id', $deleted)->delete(); - if ($count > 0) { - $this->friendlyInfo(sprintf('Removed %d old relationship(s) categories budgets and transactions.', $count)); + if (count($deletedCategories) > 0) { + $this->cleanupCategories($deletedCategories); } $this->friendlyNeutral('Validated links to deleted objects.'); } + + private function cleanupTags(array $tags): void + { + $count = DB::table('tag_transaction_journal')->whereIn('tag_id', $tags)->delete(); + if ($count > 0) { + $this->friendlyInfo(sprintf('Removed %d old relationship(s) categories transactions and tags.', $count)); + } + } + + private function cleanupJournals(array $journals): void + { + $count = DB::table('tag_transaction_journal')->whereIn('transaction_journal_id', $journals)->delete(); + if ($count > 0) { + $this->friendlyInfo(sprintf('Removed %d old relationship(s) between tags and transactions.', $count)); + } + $count = DB::table('budget_transaction_journal')->whereIn('transaction_journal_id', $journals)->delete(); + if ($count > 0) { + $this->friendlyInfo(sprintf('Removed %d old relationship(s) between budgets and transactions.', $count)); + } + $count = DB::table('category_transaction_journal')->whereIn('transaction_journal_id', $journals)->delete(); + if ($count > 0) { + $this->friendlyInfo(sprintf('Removed %d old relationship(s) categories and transactions.', $count)); + } + } + + private function cleanupBudgets(array $budgets): void + { + $count = DB::table('budget_transaction_journal')->whereIn('budget_id', $budgets)->delete(); + if ($count > 0) { + $this->friendlyInfo(sprintf('Removed %d old relationship(s) between budgets and transactions.', $count)); + } + } + + private function cleanupCategories(array $categories): void + { + $count = DB::table('category_transaction_journal')->whereIn('category_id', $categories)->delete(); + if ($count > 0) { + $this->friendlyInfo(sprintf('Removed %d old relationship(s) categories categories and transactions.', $count)); + } + } } From 6a7c35e7bcf40963dcc93d769f0128386a4f145b Mon Sep 17 00:00:00 2001 From: JC5 Date: Tue, 16 Sep 2025 20:55:27 +0200 Subject: [PATCH 15/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-09-16?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RemovesLinksToDeletedObjects.php | 6 +- composer.lock | 106 +++++++++++++++--- config/firefly.php | 4 +- package-lock.json | 28 ++--- resources/assets/v1/src/locales/it.json | 4 +- 5 files changed, 111 insertions(+), 37 deletions(-) diff --git a/app/Console/Commands/Correction/RemovesLinksToDeletedObjects.php b/app/Console/Commands/Correction/RemovesLinksToDeletedObjects.php index ea79dfa5e1..7b174418b0 100644 --- a/app/Console/Commands/Correction/RemovesLinksToDeletedObjects.php +++ b/app/Console/Commands/Correction/RemovesLinksToDeletedObjects.php @@ -1,4 +1,6 @@ whereNotNull('deleted_at')->get('tags.id')->pluck('id')->toArray(); $deletedJournals = TransactionJournal::withTrashed()->whereNotNull('deleted_at')->get('transaction_journals.id')->pluck('id')->toArray(); diff --git a/composer.lock b/composer.lock index 3bc0182e4b..ac2158c967 100644 --- a/composer.lock +++ b/composer.lock @@ -324,16 +324,16 @@ }, { "name": "dasprid/enum", - "version": "1.0.6", + "version": "1.0.7", "source": { "type": "git", "url": "https://github.com/DASPRiD/Enum.git", - "reference": "8dfd07c6d2cf31c8da90c53b83c026c7696dda90" + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/8dfd07c6d2cf31c8da90c53b83c026c7696dda90", - "reference": "8dfd07c6d2cf31c8da90c53b83c026c7696dda90", + "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/b5874fa9ed0043116c72162ec7f4fb50e02e7cce", + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce", "shasum": "" }, "require": { @@ -368,9 +368,9 @@ ], "support": { "issues": "https://github.com/DASPRiD/Enum/issues", - "source": "https://github.com/DASPRiD/Enum/tree/1.0.6" + "source": "https://github.com/DASPRiD/Enum/tree/1.0.7" }, - "time": "2024-08-09T14:30:48+00:00" + "time": "2025-09-16T12:23:56+00:00" }, { "name": "defuse/php-encryption", @@ -1878,16 +1878,16 @@ }, { "name": "laravel/framework", - "version": "v12.28.1", + "version": "v12.29.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "868c1f2d3dba4df6d21e3a8d818479f094cfd942" + "reference": "a9e4c73086f5ba38383e9c1d74b84fe46aac730b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/868c1f2d3dba4df6d21e3a8d818479f094cfd942", - "reference": "868c1f2d3dba4df6d21e3a8d818479f094cfd942", + "url": "https://api.github.com/repos/laravel/framework/zipball/a9e4c73086f5ba38383e9c1d74b84fe46aac730b", + "reference": "a9e4c73086f5ba38383e9c1d74b84fe46aac730b", "shasum": "" }, "require": { @@ -1915,6 +1915,7 @@ "monolog/monolog": "^3.0", "nesbot/carbon": "^3.8.4", "nunomaduro/termwind": "^2.0", + "phiki/phiki": "v2.0.0", "php": "^8.2", "psr/container": "^1.1.1|^2.0.1", "psr/log": "^1.0|^2.0|^3.0", @@ -2024,7 +2025,7 @@ "ext-pdo": "Required to use all database features.", "ext-posix": "Required to use all features of the queue worker.", "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0|^6.0).", - "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", + "fakerphp/faker": "Required to generate fake data using the fake() helper (^1.23).", "filp/whoops": "Required for friendly error pages in development (^2.14.3).", "laravel/tinker": "Required to use the tinker console command (^2.0).", "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).", @@ -2093,7 +2094,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-09-04T14:58:12+00:00" + "time": "2025-09-16T14:15:03+00:00" }, { "name": "laravel/passport", @@ -4349,6 +4350,77 @@ }, "time": "2020-10-15T08:29:30+00:00" }, + { + "name": "phiki/phiki", + "version": "v2.0.0", + "source": { + "type": "git", + "url": "https://github.com/phikiphp/phiki.git", + "reference": "461f6dd7e91dc3a95463b42f549ac7d0aab4702f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phikiphp/phiki/zipball/461f6dd7e91dc3a95463b42f549ac7d0aab4702f", + "reference": "461f6dd7e91dc3a95463b42f549ac7d0aab4702f", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "league/commonmark": "^2.5.3", + "php": "^8.2", + "psr/simple-cache": "^3.0" + }, + "require-dev": { + "illuminate/support": "^11.45", + "laravel/pint": "^1.18.1", + "orchestra/testbench": "^9.15", + "pestphp/pest": "^3.5.1", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.0", + "symfony/var-dumper": "^7.1.6" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Phiki\\Adapters\\Laravel\\PhikiServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Phiki\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ryan Chandler", + "email": "support@ryangjchandler.co.uk", + "homepage": "https://ryangjchandler.co.uk", + "role": "Developer" + } + ], + "description": "Syntax highlighting using TextMate grammars in PHP.", + "support": { + "issues": "https://github.com/phikiphp/phiki/issues", + "source": "https://github.com/phikiphp/phiki/tree/v2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/ryangjchandler", + "type": "github" + }, + { + "url": "https://buymeacoffee.com/ryangjchandler", + "type": "other" + } + ], + "time": "2025-08-28T18:20:27+00:00" + }, { "name": "php-http/client-common", "version": "2.7.2", @@ -11332,16 +11404,16 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.25", + "version": "2.1.26", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "4087d28bd252895874e174d65e26b2c202ed893a" + "reference": "b13345001a8553ec405b7741be0c6b8d7f8b5bf5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/4087d28bd252895874e174d65e26b2c202ed893a", - "reference": "4087d28bd252895874e174d65e26b2c202ed893a", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b13345001a8553ec405b7741be0c6b8d7f8b5bf5", + "reference": "b13345001a8553ec405b7741be0c6b8d7f8b5bf5", "shasum": "" }, "require": { @@ -11386,7 +11458,7 @@ "type": "github" } ], - "time": "2025-09-12T14:26:42+00:00" + "time": "2025-09-16T11:33:46+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", diff --git a/config/firefly.php b/config/firefly.php index 7fa9eff12a..d5c69546c3 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -78,8 +78,8 @@ return [ 'running_balance_column' => env('USE_RUNNING_BALANCE', false), // see cer.php for exchange rates feature flag. ], - 'version' => 'develop/2025-09-15', - 'build_time' => 1757957039, + 'version' => 'develop/2025-09-16', + 'build_time' => 1758048808, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 26, diff --git a/package-lock.json b/package-lock.json index b8d603f07b..671dd29016 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3159,13 +3159,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.4.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.4.0.tgz", - "integrity": "sha512-gUuVEAK4/u6F9wRLznPUU4WGUacSEBDPoC2TrBkw3GAnOLHBL45QdfHOXp1kJ4ypBGLxTOB+t7NJLpKoC3gznQ==", + "version": "24.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.0.tgz", + "integrity": "sha512-y1dMvuvJspJiPSDZUQ+WMBvF7dpnEqN4x9DDC9ie5Fs/HUZJA3wFp7EhHoVaKX/iI0cRoECV8X2jL8zi0xrHCg==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.11.0" + "undici-types": "~7.12.0" } }, "node_modules/@types/node-forge": { @@ -4347,9 +4347,9 @@ } }, "node_modules/browserslist": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.0.tgz", - "integrity": "sha512-P9go2WrP9FiPwLv3zqRD/Uoxo0RSHjzFCiQz7d4vbmwNqQFo9T9WCeP/Qn5EbcKQY6DBbkxEXNcpJOmncNrb7A==", + "version": "4.26.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", + "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==", "dev": true, "funding": [ { @@ -4367,7 +4367,7 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.8.2", + "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001741", "electron-to-chromium": "^1.5.218", "node-releases": "^2.0.21", @@ -4508,9 +4508,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001741", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001741.tgz", - "integrity": "sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==", + "version": "1.0.30001743", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz", + "integrity": "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==", "dev": true, "funding": [ { @@ -11357,9 +11357,9 @@ } }, "node_modules/undici-types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.11.0.tgz", - "integrity": "sha512-kt1ZriHTi7MU+Z/r9DOdAI3ONdaR3M3csEaRc6ewa4f4dTvX4cQCbJ4NkEn0ohE4hHtq85+PhPSTY+pO/1PwgA==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz", + "integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==", "dev": true, "license": "MIT" }, diff --git a/resources/assets/v1/src/locales/it.json b/resources/assets/v1/src/locales/it.json index dc449681e6..08b853b545 100644 --- a/resources/assets/v1/src/locales/it.json +++ b/resources/assets/v1/src/locales/it.json @@ -160,7 +160,7 @@ "url": "URL", "active": "Attivo", "interest_date": "Data di valuta", - "administration_currency": "Primary currency", + "administration_currency": "Valuta primaria", "title": "Titolo", "date": "Data", "book_date": "Data contabile", @@ -180,7 +180,7 @@ "list": { "title": "Titolo", "active": "\u00c8 attivo?", - "primary_currency": "Primary currency", + "primary_currency": "Valuta primaria", "trigger": "Trigger", "response": "Risposta", "delivery": "Consegna", From cb6b3d5f856e7785b66ae5043f95eca712c56d9f Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 16 Sep 2025 21:09:29 +0200 Subject: [PATCH 16/91] Fix #10891 --- app/Models/CurrencyExchangeRate.php | 2 +- app/Support/Cronjobs/AutoBudgetCronjob.php | 4 ++-- app/Support/Cronjobs/BillWarningCronjob.php | 4 ++-- app/Support/Cronjobs/ExchangeRatesCronjob.php | 4 ++-- app/Support/Cronjobs/RecurringCronjob.php | 4 ++-- app/Support/Cronjobs/WebhookCronjob.php | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/Models/CurrencyExchangeRate.php b/app/Models/CurrencyExchangeRate.php index c484ae2cc5..7edec3b797 100644 --- a/app/Models/CurrencyExchangeRate.php +++ b/app/Models/CurrencyExchangeRate.php @@ -38,7 +38,7 @@ class CurrencyExchangeRate extends Model use ReturnsIntegerUserIdTrait; use SoftDeletes; - protected $fillable = ['user_id', 'from_currency_id', 'to_currency_id', 'date', 'date_tz', 'rate']; + protected $fillable = ['user_id','user_group_id', 'from_currency_id', 'to_currency_id', 'date', 'date_tz', 'rate']; public function fromCurrency(): BelongsTo { diff --git a/app/Support/Cronjobs/AutoBudgetCronjob.php b/app/Support/Cronjobs/AutoBudgetCronjob.php index f71a4a4da1..9a2a5a0d66 100644 --- a/app/Support/Cronjobs/AutoBudgetCronjob.php +++ b/app/Support/Cronjobs/AutoBudgetCronjob.php @@ -40,8 +40,8 @@ class AutoBudgetCronjob extends AbstractCronjob /** @var Configuration $config */ $config = FireflyConfig::get('last_ab_job', 0); $lastTime = (int) $config->data; - $diff = Carbon::now()->getTimestamp() - $lastTime; - $diffForHumans = today(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true); + $diff = now(config('app.timezone'))->getTimestamp() - $lastTime; + $diffForHumans = now(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true); if (0 === $lastTime) { Log::info('Auto budget cron-job has never fired before.'); } diff --git a/app/Support/Cronjobs/BillWarningCronjob.php b/app/Support/Cronjobs/BillWarningCronjob.php index aece4d403f..8a30bb9c0a 100644 --- a/app/Support/Cronjobs/BillWarningCronjob.php +++ b/app/Support/Cronjobs/BillWarningCronjob.php @@ -46,8 +46,8 @@ class BillWarningCronjob extends AbstractCronjob /** @var Configuration $config */ $config = FireflyConfig::get('last_bw_job', 0); $lastTime = (int) $config->data; - $diff = Carbon::now()->getTimestamp() - $lastTime; - $diffForHumans = today(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true); + $diff = now(config('app.timezone'))->getTimestamp() - $lastTime; + $diffForHumans = now(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true); if (0 === $lastTime) { Log::info('The bill notification cron-job has never fired before.'); diff --git a/app/Support/Cronjobs/ExchangeRatesCronjob.php b/app/Support/Cronjobs/ExchangeRatesCronjob.php index f7d80b8033..71c9a8e587 100644 --- a/app/Support/Cronjobs/ExchangeRatesCronjob.php +++ b/app/Support/Cronjobs/ExchangeRatesCronjob.php @@ -40,8 +40,8 @@ class ExchangeRatesCronjob extends AbstractCronjob /** @var Configuration $config */ $config = FireflyConfig::get('last_cer_job', 0); $lastTime = (int) $config->data; - $diff = Carbon::now()->getTimestamp() - $lastTime; - $diffForHumans = today(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true); + $diff = now(config('app.timezone'))->getTimestamp() - $lastTime; + $diffForHumans = now(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true); if (0 === $lastTime) { Log::info('Exchange rates cron-job has never fired before.'); } diff --git a/app/Support/Cronjobs/RecurringCronjob.php b/app/Support/Cronjobs/RecurringCronjob.php index cc6bb7f901..c722733253 100644 --- a/app/Support/Cronjobs/RecurringCronjob.php +++ b/app/Support/Cronjobs/RecurringCronjob.php @@ -46,8 +46,8 @@ class RecurringCronjob extends AbstractCronjob /** @var Configuration $config */ $config = FireflyConfig::get('last_rt_job', 0); $lastTime = (int) $config->data; - $diff = Carbon::now()->getTimestamp() - $lastTime; - $diffForHumans = today(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true); + $diff = now(config('app.timezone'))->getTimestamp() - $lastTime; + $diffForHumans = now(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true); if (0 === $lastTime) { Log::info('Recurring transactions cron-job has never fired before.'); diff --git a/app/Support/Cronjobs/WebhookCronjob.php b/app/Support/Cronjobs/WebhookCronjob.php index dcac057140..0cd1899380 100644 --- a/app/Support/Cronjobs/WebhookCronjob.php +++ b/app/Support/Cronjobs/WebhookCronjob.php @@ -46,8 +46,8 @@ class WebhookCronjob extends AbstractCronjob /** @var Configuration $config */ $config = FireflyConfig::get('last_webhook_job', 0); $lastTime = (int) $config->data; - $diff = Carbon::now()->getTimestamp() - $lastTime; - $diffForHumans = today(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true); + $diff = now(config('app.timezone'))->getTimestamp() - $lastTime; + $diffForHumans = now(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true); if (0 === $lastTime) { Log::info('The webhook cron-job has never fired before.'); From 3491fbb99dc131736c92a0a3d7376049224261ff Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 17 Sep 2025 07:09:40 +0200 Subject: [PATCH 17/91] Force account search to validate it did not just find the source account. #10920 --- app/Factory/TransactionJournalFactory.php | 2 +- .../Account/AccountRepository.php | 13 ++-- .../Internal/Support/JournalServiceTrait.php | 74 ++++++++++++------- app/Validation/Account/DepositValidation.php | 34 +++++---- app/Validation/Account/OBValidation.php | 23 +++--- .../Account/WithdrawalValidation.php | 25 ++++--- app/Validation/AccountValidator.php | 43 +++++------ resources/lang/en_US/validation.php | 12 +-- 8 files changed, 126 insertions(+), 100 deletions(-) diff --git a/app/Factory/TransactionJournalFactory.php b/app/Factory/TransactionJournalFactory.php index 7206947be2..dd2d9e6830 100644 --- a/app/Factory/TransactionJournalFactory.php +++ b/app/Factory/TransactionJournalFactory.php @@ -222,7 +222,7 @@ class TransactionJournalFactory Log::debug('Source info:', $sourceInfo); Log::debug('Destination info:', $destInfo); $sourceAccount = $this->getAccount($type->type, 'source', $sourceInfo); - $destinationAccount = $this->getAccount($type->type, 'destination', $destInfo); + $destinationAccount = $this->getAccount($type->type, 'destination', $destInfo, $sourceAccount); Log::debug('Done with getAccount(2x)'); diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index c9039b7c0f..c2a14d8d81 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -45,6 +45,7 @@ use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; use Override; @@ -150,18 +151,18 @@ class AccountRepository implements AccountRepositoryInterface, UserGroupInterfac $query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); $query->whereIn('account_types.type', $types); } - app('log')->debug(sprintf('Searching for account named "%s" (of user #%d) of the following type(s)', $name, $this->user->id), ['types' => $types]); + Log::debug(sprintf('Searching for account named "%s" (of user #%d) of the following type(s)', $name, $this->user->id), ['types' => $types]); $query->where('accounts.name', $name); /** @var null|Account $account */ $account = $query->first(['accounts.*']); if (null === $account) { - app('log')->debug(sprintf('There is no account with name "%s" of types', $name), $types); + Log::debug(sprintf('There is no account with name "%s" of types', $name), $types); return null; } - app('log')->debug(sprintf('Found #%d (%s) with type id %d', $account->id, $account->name, $account->account_type_id)); + Log::debug(sprintf('Found #%d (%s) with type id %d', $account->id, $account->name, $account->account_type_id)); return $account; } @@ -465,14 +466,14 @@ class AccountRepository implements AccountRepositoryInterface, UserGroupInterfac ]; if (array_key_exists(ucfirst($type), $sets)) { $order = (int) $this->getAccountsByType($sets[ucfirst($type)])->max('order'); - app('log')->debug(sprintf('Return max order of "%s" set: %d', $type, $order)); + Log::debug(sprintf('Return max order of "%s" set: %d', $type, $order)); return $order; } $specials = [AccountTypeEnum::CASH->value, AccountTypeEnum::INITIAL_BALANCE->value, AccountTypeEnum::IMPORT->value, AccountTypeEnum::RECONCILIATION->value]; $order = (int) $this->getAccountsByType($specials)->max('order'); - app('log')->debug(sprintf('Return max order of "%s" set (specials!): %d', $type, $order)); + Log::debug(sprintf('Return max order of "%s" set (specials!): %d', $type, $order)); return $order; } @@ -599,7 +600,7 @@ class AccountRepository implements AccountRepositoryInterface, UserGroupInterfac continue; } if ($index !== (int) $account->order) { - app('log')->debug(sprintf('Account #%d ("%s"): order should %d be but is %d.', $account->id, $account->name, $index, $account->order)); + Log::debug(sprintf('Account #%d ("%s"): order should %d be but is %d.', $account->id, $account->name, $index, $account->order)); $account->order = $index; $account->save(); } diff --git a/app/Services/Internal/Support/JournalServiceTrait.php b/app/Services/Internal/Support/JournalServiceTrait.php index d0de5db9df..9d7cedad90 100644 --- a/app/Services/Internal/Support/JournalServiceTrait.php +++ b/app/Services/Internal/Support/JournalServiceTrait.php @@ -54,7 +54,7 @@ trait JournalServiceTrait /** * @throws FireflyException */ - protected function getAccount(string $transactionType, string $direction, array $data): ?Account + protected function getAccount(string $transactionType, string $direction, array $data, ?Account $opposite = null): ?Account { // some debug logging: Log::debug(sprintf('Now in getAccount(%s)', $direction), $data); @@ -69,12 +69,12 @@ trait JournalServiceTrait $message = 'Transaction = %s, %s account should be in: %s. Direction is %s.'; Log::debug(sprintf($message, $transactionType, $direction, implode(', ', $expectedTypes[$transactionType] ?? ['UNKNOWN']), $direction)); - $result = $this->findAccountById($data, $expectedTypes[$transactionType]); - $result = $this->findAccountByIban($result, $data, $expectedTypes[$transactionType]); + $result = $this->findAccountById($data, $expectedTypes[$transactionType], $opposite); + $result = $this->findAccountByIban($result, $data, $expectedTypes[$transactionType], $opposite); $ibanResult = $result; - $result = $this->findAccountByNumber($result, $data, $expectedTypes[$transactionType]); + $result = $this->findAccountByNumber($result, $data, $expectedTypes[$transactionType], $opposite); $numberResult = $result; - $result = $this->findAccountByName($result, $data, $expectedTypes[$transactionType]); + $result = $this->findAccountByName($result, $data, $expectedTypes[$transactionType], $opposite); $nameResult = $result; // if $result (find by name) is NULL, but IBAN is set, any result of the search by NAME can't overrule @@ -82,7 +82,7 @@ trait JournalServiceTrait if (null !== $nameResult && null === $numberResult && null === $ibanResult && '' !== (string) $data['iban'] && '' !== (string) $nameResult->iban) { $data['name'] = sprintf('%s (%s)', $data['name'], $data['iban']); Log::debug(sprintf('Search again using the new name, "%s".', $data['name'])); - $result = $this->findAccountByName(null, $data, $expectedTypes[$transactionType]); + $result = $this->findAccountByName(null, $data, $expectedTypes[$transactionType], $opposite); } // the account that Firefly III creates must be "creatable", aka select the one we can create from the list just in case @@ -115,15 +115,18 @@ trait JournalServiceTrait return $result; } - private function findAccountById(array $data, array $types): ?Account + private function findAccountById(array $data, array $types, ?Account $opposite = null): ?Account { // first attempt, find by ID. if (null !== $data['id']) { $search = $this->accountRepository->find((int) $data['id']); if (null !== $search && in_array($search->accountType->type, $types, true)) { - Log::debug( - sprintf('Found "account_id" object: #%d, "%s" of type %s (1)', $search->id, $search->name, $search->accountType->type) - ); + Log::debug(sprintf('Found "account_id" object: #%d, "%s" of type %s (1)', $search->id, $search->name, $search->accountType->type)); + + if($opposite?->id === $search->id) { + Log::debug(sprintf('Account #%d is the same as opposite account #%d, returning NULL.', $search->id, $opposite->id)); + return null; + } return $search; } @@ -140,7 +143,7 @@ trait JournalServiceTrait return null; } - private function findAccountByIban(?Account $account, array $data, array $types): ?Account + private function findAccountByIban(?Account $account, array $data, array $types, ?Account $opposite = null): ?Account { if ($account instanceof Account) { Log::debug(sprintf('Already have account #%d ("%s"), return that.', $account->id, $account->name)); @@ -153,21 +156,26 @@ trait JournalServiceTrait return null; } // find by preferred type. - $source = $this->accountRepository->findByIbanNull($data['iban'], [$types[0]]); + $result = $this->accountRepository->findByIbanNull($data['iban'], [$types[0]]); // or any expected type. - $source ??= $this->accountRepository->findByIbanNull($data['iban'], $types); + $result ??= $this->accountRepository->findByIbanNull($data['iban'], $types); - if (null !== $source) { - Log::debug(sprintf('Found "account_iban" object: #%d, %s', $source->id, $source->name)); + if (null !== $result) { + Log::debug(sprintf('Found "account_iban" object: #%d, %s', $result->id, $result->name)); - return $source; + if($opposite?->id === $result->id) { + Log::debug(sprintf('Account #%d is the same as opposite account #%d, returning NULL.', $result->id, $opposite->id)); + return null; + } + + return $result; } Log::debug(sprintf('Found no account with IBAN "%s" of expected types', $data['iban']), $types); return null; } - private function findAccountByNumber(?Account $account, array $data, array $types): ?Account + private function findAccountByNumber(?Account $account, array $data, array $types, ?Account $opposite = null): ?Account { if ($account instanceof Account) { Log::debug(sprintf('Already have account #%d ("%s"), return that.', $account->id, $account->name)); @@ -180,15 +188,20 @@ trait JournalServiceTrait return null; } // find by preferred type. - $source = $this->accountRepository->findByAccountNumber((string) $data['number'], [$types[0]]); + $result = $this->accountRepository->findByAccountNumber((string) $data['number'], [$types[0]]); // or any expected type. - $source ??= $this->accountRepository->findByAccountNumber((string) $data['number'], $types); + $result ??= $this->accountRepository->findByAccountNumber((string) $data['number'], $types); - if (null !== $source) { - Log::debug(sprintf('Found account: #%d, %s', $source->id, $source->name)); + if (null !== $result) { + Log::debug(sprintf('Found account: #%d, %s', $result->id, $result->name)); - return $source; + if($opposite?->id === $result->id) { + Log::debug(sprintf('Account #%d is the same as opposite account #%d, returning NULL.', $result->id, $opposite->id)); + return null; + } + + return $result; } Log::debug(sprintf('Found no account with account number "%s" of expected types', $data['number']), $types); @@ -196,7 +209,7 @@ trait JournalServiceTrait return null; } - private function findAccountByName(?Account $account, array $data, array $types): ?Account + private function findAccountByName(?Account $account, array $data, array $types, ?Account $opposite = null): ?Account { if ($account instanceof Account) { Log::debug(sprintf('Already have account #%d ("%s"), return that.', $account->id, $account->name)); @@ -210,15 +223,20 @@ trait JournalServiceTrait } // find by preferred type. - $source = $this->accountRepository->findByName($data['name'], [$types[0]]); + $result = $this->accountRepository->findByName($data['name'], [$types[0]]); // or any expected type. - $source ??= $this->accountRepository->findByName($data['name'], $types); + $result ??= $this->accountRepository->findByName($data['name'], $types); - if (null !== $source) { - Log::debug(sprintf('Found "account_name" object: #%d, %s', $source->id, $source->name)); + if (null !== $result) { + Log::debug(sprintf('Found "account_name" object: #%d, %s', $result->id, $result->name)); - return $source; + if($opposite?->id === $result->id) { + Log::debug(sprintf('Account #%d is the same as opposite account #%d, returning NULL.', $result->id, $opposite->id)); + return null; + } + + return $result; } Log::debug(sprintf('Found no account with account name "%s" of expected types', $data['name']), $types); diff --git a/app/Validation/Account/DepositValidation.php b/app/Validation/Account/DepositValidation.php index 87e2e69f43..42a38447b6 100644 --- a/app/Validation/Account/DepositValidation.php +++ b/app/Validation/Account/DepositValidation.php @@ -27,6 +27,7 @@ namespace FireflyIII\Validation\Account; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; +use Illuminate\Support\Facades\Log; /** * Trait DepositValidation @@ -40,7 +41,7 @@ trait DepositValidation $accountName = array_key_exists('name', $array) ? $array['name'] : null; $accountIban = array_key_exists('iban', $array) ? $array['iban'] : null; - app('log')->debug('Now in validateDepositDestination', $array); + Log::debug('Now in validateDepositDestination', $array); // source can be any of the following types. $validTypes = $this->combinations[$this->transactionType][$this->source->accountType->type] ?? []; @@ -48,12 +49,12 @@ trait DepositValidation // if both values are NULL we return false, // because the destination of a deposit can't be created. $this->destError = (string) trans('validation.deposit_dest_need_data'); - app('log')->error('Both values are NULL, cant create deposit destination.'); + Log::error('Both values are NULL, cant create deposit destination.'); $result = false; } // if the account can be created anyway we don't need to search. if (null === $result && true === $this->canCreateTypes($validTypes)) { - app('log')->debug('Can create some of these types, so return true.'); + Log::debug('Can create some of these types, so return true.'); $result = true; } @@ -61,17 +62,17 @@ trait DepositValidation // otherwise try to find the account: $search = $this->findExistingAccount($validTypes, $array); if (null === $search) { - app('log')->debug('findExistingAccount() returned NULL, so the result is false.'); + Log::debug('findExistingAccount() returned NULL, so the result is false.'); $this->destError = (string) trans('validation.deposit_dest_bad_data', ['id' => $accountId, 'name' => $accountName]); $result = false; } if (null !== $search) { - app('log')->debug(sprintf('findExistingAccount() returned #%d ("%s"), so the result is true.', $search->id, $search->name)); + Log::debug(sprintf('findExistingAccount() returned #%d ("%s"), so the result is true.', $search->id, $search->name)); $this->setDestination($search); $result = true; } } - app('log')->debug(sprintf('validateDepositDestination will return %s', var_export($result, true))); + Log::debug(sprintf('validateDepositDestination will return %s', var_export($result, true))); return $result; } @@ -92,7 +93,7 @@ trait DepositValidation $accountName = array_key_exists('name', $array) ? $array['name'] : null; $accountIban = array_key_exists('iban', $array) ? $array['iban'] : null; $accountNumber = array_key_exists('number', $array) ? $array['number'] : null; - app('log')->debug('Now in validateDepositSource', $array); + Log::debug('Now in validateDepositSource', $array); // null = we found nothing at all or didn't even search // false = invalid results @@ -114,7 +115,7 @@ trait DepositValidation // if there is an iban, it can only be in use by a valid source type, or we will fail. if (null !== $accountIban && '' !== $accountIban) { - app('log')->debug('Check if there is not already another account with this IBAN'); + Log::debug('Check if there is not already another account with this IBAN'); $existing = $this->findExistingAccount($validTypes, ['iban' => $accountIban], true); if (null !== $existing) { $this->sourceError = (string) trans('validation.deposit_src_iban_exists'); @@ -128,11 +129,14 @@ trait DepositValidation if (null !== $accountId) { $search = $this->accountRepository->find($accountId); if (null !== $search && !in_array($search->accountType->type, $validTypes, true)) { - app('log')->debug(sprintf('User submitted an ID (#%d), which is a "%s", so this is not a valid source.', $accountId, $search->accountType->type)); - app('log')->debug(sprintf('Firefly III accepts ID #%d as valid account data.', $accountId)); + Log::debug(sprintf('User submitted an ID (#%d), which is a "%s", so this is not a valid source.', $accountId, $search->accountType->type)); + Log::debug(sprintf('Firefly III does not accept ID #%d as valid account data.', $accountId)); + // #10921 Set result false + $this->sourceError = (string) trans('validation.withdrawal_source_bad_data', ['id' => $accountId, 'name' => $accountName]); + $result = false; } if (null !== $search && in_array($search->accountType->type, $validTypes, true)) { - app('log')->debug('ID result is not null and seems valid, save as source account.'); + Log::debug('ID result is not null and seems valid, save as source account.'); $this->setSource($search); $result = true; } @@ -142,11 +146,11 @@ trait DepositValidation if (null !== $accountIban) { $search = $this->accountRepository->findByIbanNull($accountIban, $validTypes); if (null !== $search && !in_array($search->accountType->type, $validTypes, true)) { - app('log')->debug(sprintf('User submitted IBAN ("%s"), which is a "%s", so this is not a valid source.', $accountIban, $search->accountType->type)); + Log::debug(sprintf('User submitted IBAN ("%s"), which is a "%s", so this is not a valid source.', $accountIban, $search->accountType->type)); $result = false; } if (null !== $search && in_array($search->accountType->type, $validTypes, true)) { - app('log')->debug('IBAN result is not null and seems valid, save as source account.'); + Log::debug('IBAN result is not null and seems valid, save as source account.'); $this->setSource($search); $result = true; } @@ -156,13 +160,13 @@ trait DepositValidation if (null !== $accountNumber && '' !== $accountNumber) { $search = $this->accountRepository->findByAccountNumber($accountNumber, $validTypes); if (null !== $search && !in_array($search->accountType->type, $validTypes, true)) { - app('log')->debug( + Log::debug( sprintf('User submitted number ("%s"), which is a "%s", so this is not a valid source.', $accountNumber, $search->accountType->type) ); $result = false; } if (null !== $search && in_array($search->accountType->type, $validTypes, true)) { - app('log')->debug('Number result is not null and seems valid, save as source account.'); + Log::debug('Number result is not null and seems valid, save as source account.'); $this->setSource($search); $result = true; } diff --git a/app/Validation/Account/OBValidation.php b/app/Validation/Account/OBValidation.php index a3f32afeb0..b0633c63f9 100644 --- a/app/Validation/Account/OBValidation.php +++ b/app/Validation/Account/OBValidation.php @@ -27,6 +27,7 @@ namespace FireflyIII\Validation\Account; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; +use Illuminate\Support\Facades\Log; /** * Trait OBValidation @@ -38,7 +39,7 @@ trait OBValidation $result = null; $accountId = array_key_exists('id', $array) ? $array['id'] : null; $accountName = array_key_exists('name', $array) ? $array['name'] : null; - app('log')->debug('Now in validateOBDestination', $array); + Log::debug('Now in validateOBDestination', $array); // source can be any of the following types. $validTypes = $this->combinations[$this->transactionType][$this->source?->accountType->type] ?? []; @@ -46,12 +47,12 @@ trait OBValidation // if both values are NULL we return false, // because the destination of a deposit can't be created. $this->destError = (string) trans('validation.ob_dest_need_data'); - app('log')->error('Both values are NULL, cant create OB destination.'); + Log::error('Both values are NULL, cant create OB destination.'); $result = false; } // if the account can be created anyway we don't need to search. if (null === $result && true === $this->canCreateTypes($validTypes)) { - app('log')->debug('Can create some of these types, so return true.'); + Log::debug('Can create some of these types, so return true.'); $result = true; } @@ -59,17 +60,17 @@ trait OBValidation // otherwise try to find the account: $search = $this->findExistingAccount($validTypes, $array); if (null === $search) { - app('log')->debug('findExistingAccount() returned NULL, so the result is false.', $validTypes); + Log::debug('findExistingAccount() returned NULL, so the result is false.', $validTypes); $this->destError = (string) trans('validation.ob_dest_bad_data', ['id' => $accountId, 'name' => $accountName]); $result = false; } if (null !== $search) { - app('log')->debug(sprintf('findExistingAccount() returned #%d ("%s"), so the result is true.', $search->id, $search->name)); + Log::debug(sprintf('findExistingAccount() returned #%d ("%s"), so the result is true.', $search->id, $search->name)); $this->setDestination($search); $result = true; } } - app('log')->debug(sprintf('validateOBDestination(%d, "%s") will return %s', $accountId, $accountName, var_export($result, true))); + Log::debug(sprintf('validateOBDestination(%d, "%s") will return %s', $accountId, $accountName, var_export($result, true))); return $result; } @@ -84,7 +85,7 @@ trait OBValidation { $accountId = array_key_exists('id', $array) ? $array['id'] : null; $accountName = array_key_exists('name', $array) ? $array['name'] : null; - app('log')->debug('Now in validateOBSource', $array); + Log::debug('Now in validateOBSource', $array); $result = null; // source can be any of the following types. $validTypes = array_keys($this->combinations[$this->transactionType]); @@ -100,19 +101,19 @@ trait OBValidation // if the user submits an ID only but that ID is not of the correct type, // return false. if (null !== $accountId && null === $accountName) { - app('log')->debug('Source ID is not null, but name is null.'); + Log::debug('Source ID is not null, but name is null.'); $search = $this->accountRepository->find($accountId); // the source resulted in an account, but it's not of a valid type. if (null !== $search && !in_array($search->accountType->type, $validTypes, true)) { $message = sprintf('User submitted only an ID (#%d), which is a "%s", so this is not a valid source.', $accountId, $search->accountType->type); - app('log')->debug($message); + Log::debug($message); $this->sourceError = $message; $result = false; } // the source resulted in an account, AND it's of a valid type. if (null !== $search && in_array($search->accountType->type, $validTypes, true)) { - app('log')->debug(sprintf('Found account of correct type: #%d, "%s"', $search->id, $search->name)); + Log::debug(sprintf('Found account of correct type: #%d, "%s"', $search->id, $search->name)); $this->setSource($search); $result = true; } @@ -120,7 +121,7 @@ trait OBValidation // if the account can be created anyway we don't need to search. if (null === $result && true === $this->canCreateTypes($validTypes)) { - app('log')->debug('Result is still null.'); + Log::debug('Result is still null.'); $result = true; // set the source to be a (dummy) initial balance account. diff --git a/app/Validation/Account/WithdrawalValidation.php b/app/Validation/Account/WithdrawalValidation.php index 9456e4ecf9..ac2a06d825 100644 --- a/app/Validation/Account/WithdrawalValidation.php +++ b/app/Validation/Account/WithdrawalValidation.php @@ -26,6 +26,7 @@ namespace FireflyIII\Validation\Account; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Models\Account; +use Illuminate\Support\Facades\Log; /** * Trait WithdrawalValidation @@ -37,14 +38,14 @@ trait WithdrawalValidation $accountId = array_key_exists('id', $array) ? $array['id'] : null; $accountName = array_key_exists('name', $array) ? $array['name'] : null; $accountIban = array_key_exists('iban', $array) ? $array['iban'] : null; - app('log')->debug('Now in validateGenericSource', $array); + Log::debug('Now in validateGenericSource', $array); // source can be any of the following types. $validTypes = [AccountTypeEnum::ASSET->value, AccountTypeEnum::REVENUE->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value]; if (null === $accountId && null === $accountName && null === $accountIban && false === $this->canCreateTypes($validTypes)) { // if both values are NULL we return TRUE // because we assume the user doesn't want to submit / change anything. $this->sourceError = (string) trans('validation.withdrawal_source_need_data'); - app('log')->warning('[a] Not a valid source. Need more data.'); + Log::warning('[a] Not a valid source. Need more data.'); return false; } @@ -53,12 +54,12 @@ trait WithdrawalValidation $search = $this->findExistingAccount($validTypes, $array); if (null === $search) { $this->sourceError = (string) trans('validation.withdrawal_source_bad_data', ['id' => $accountId, 'name' => $accountName]); - app('log')->warning('Not a valid source. Cant find it.', $validTypes); + Log::warning('Not a valid source. Cant find it.', $validTypes); return false; } $this->setSource($search); - app('log')->debug('Valid source account!'); + Log::debug('Valid source account!'); return true; } @@ -73,10 +74,10 @@ trait WithdrawalValidation $accountName = array_key_exists('name', $array) ? $array['name'] : null; $accountIban = array_key_exists('iban', $array) ? $array['iban'] : null; $accountNumber = array_key_exists('number', $array) ? $array['number'] : null; - app('log')->debug('Now in validateWithdrawalDestination()', $array); + Log::debug('Now in validateWithdrawalDestination()', $array); // source can be any of the following types. $validTypes = $this->combinations[$this->transactionType][$this->source->accountType->type] ?? []; - app('log')->debug('Source type can be: ', $validTypes); + Log::debug('Source type can be: ', $validTypes); if (null === $accountId && null === $accountName && null === $accountIban && null === $accountNumber && false === $this->canCreateTypes($validTypes)) { // if both values are NULL return false, // because the destination of a withdrawal can never be created automatically. @@ -86,7 +87,7 @@ trait WithdrawalValidation } // if there's an ID it must be of the "validTypes". - if (null !== $accountId && 0 !== $accountId) { + if (null !== $accountId && 0 !== $accountId && $accountId !== $this->source->id) { $found = $this->accountRepository->find($accountId); if (null !== $found) { $type = $found->accountType->type; @@ -104,7 +105,7 @@ trait WithdrawalValidation // if there is an iban, it can only be in use by a valid destination type, or we will fail. // the inverse of $validTypes is if (null !== $accountIban && '' !== $accountIban) { - app('log')->debug('Check if there is not already an account with this IBAN'); + Log::debug('Check if there is not already an account with this IBAN'); // the inverse flag reverses the search, searching for everything that is NOT a valid type. $existing = $this->findExistingAccount($validTypes, ['iban' => $accountIban], true); if (null !== $existing) { @@ -125,14 +126,14 @@ trait WithdrawalValidation $accountIban = array_key_exists('iban', $array) ? $array['iban'] : null; $accountNumber = array_key_exists('number', $array) ? $array['number'] : null; - app('log')->debug('Now in validateWithdrawalSource', $array); + Log::debug('Now in validateWithdrawalSource', $array); // source can be any of the following types. $validTypes = array_keys($this->combinations[$this->transactionType]); if (null === $accountId && null === $accountName && null === $accountNumber && null === $accountIban && false === $this->canCreateTypes($validTypes)) { // if both values are NULL we return false, // because the source of a withdrawal can't be created. $this->sourceError = (string) trans('validation.withdrawal_source_need_data'); - app('log')->warning('[b] Not a valid source. Need more data.'); + Log::warning('[b] Not a valid source. Need more data.'); return false; } @@ -141,12 +142,12 @@ trait WithdrawalValidation $search = $this->findExistingAccount($validTypes, $array); if (null === $search) { $this->sourceError = (string) trans('validation.withdrawal_source_bad_data', ['id' => $accountId, 'name' => $accountName]); - app('log')->warning('Not a valid source. Cant find it.', $validTypes); + Log::warning('Not a valid source. Cant find it.', $validTypes); return false; } $this->setSource($search); - app('log')->debug('Valid source account!'); + Log::debug('Valid source account!'); return true; } diff --git a/app/Validation/AccountValidator.php b/app/Validation/AccountValidator.php index 44c9ad9d7b..0605b82bf0 100644 --- a/app/Validation/AccountValidator.php +++ b/app/Validation/AccountValidator.php @@ -36,6 +36,7 @@ use FireflyIII\Validation\Account\OBValidation; use FireflyIII\Validation\Account\ReconciliationValidation; use FireflyIII\Validation\Account\TransferValidation; use FireflyIII\Validation\Account\WithdrawalValidation; +use Illuminate\Support\Facades\Log; /** * Class AccountValidator @@ -80,10 +81,10 @@ class AccountValidator public function setSource(?Account $account): void { if (!$account instanceof Account) { - app('log')->debug('AccountValidator source is set to NULL'); + Log::debug('AccountValidator source is set to NULL'); } if ($account instanceof Account) { - app('log')->debug(sprintf('AccountValidator source is set to #%d: "%s" (%s)', $account->id, $account->name, $account->accountType?->type)); + Log::debug(sprintf('AccountValidator source is set to #%d: "%s" (%s)', $account->id, $account->name, $account->accountType?->type)); } $this->source = $account; } @@ -91,17 +92,17 @@ class AccountValidator public function setDestination(?Account $account): void { if (!$account instanceof Account) { - app('log')->debug('AccountValidator destination is set to NULL'); + Log::debug('AccountValidator destination is set to NULL'); } if ($account instanceof Account) { - app('log')->debug(sprintf('AccountValidator destination is set to #%d: "%s" (%s)', $account->id, $account->name, $account->accountType->type)); + Log::debug(sprintf('AccountValidator destination is set to #%d: "%s" (%s)', $account->id, $account->name, $account->accountType->type)); } $this->destination = $account; } public function setTransactionType(string $transactionType): void { - app('log')->debug(sprintf('Transaction type for validator is now "%s".', ucfirst($transactionType))); + Log::debug(sprintf('Transaction type for validator is now "%s".', ucfirst($transactionType))); $this->transactionType = ucfirst($transactionType); } @@ -117,9 +118,9 @@ class AccountValidator public function validateDestination(array $array): bool { - app('log')->debug('Now in AccountValidator::validateDestination()', $array); + Log::debug('Now in AccountValidator::validateDestination()', $array); if (!$this->source instanceof Account) { - app('log')->error('Source is NULL, always FALSE.'); + Log::error('Source is NULL, always FALSE.'); $this->destError = 'No source account validation has taken place yet. Please do this first or overrule the object.'; return false; @@ -128,7 +129,7 @@ class AccountValidator switch ($this->transactionType) { default: $this->destError = sprintf('AccountValidator::validateDestination cannot handle "%s", so it will always return false.', $this->transactionType); - app('log')->error(sprintf('AccountValidator::validateDestination cannot handle "%s", so it will always return false.', $this->transactionType)); + Log::error(sprintf('AccountValidator::validateDestination cannot handle "%s", so it will always return false.', $this->transactionType)); $result = false; @@ -170,11 +171,11 @@ class AccountValidator public function validateSource(array $array): bool { - app('log')->debug('Now in AccountValidator::validateSource()', $array); + Log::debug('Now in AccountValidator::validateSource()', $array); switch ($this->transactionType) { default: - app('log')->error(sprintf('AccountValidator::validateSource cannot handle "%s", so it will do a generic check.', $this->transactionType)); + Log::error(sprintf('AccountValidator::validateSource cannot handle "%s", so it will do a generic check.', $this->transactionType)); $result = $this->validateGenericSource($array); break; @@ -205,7 +206,7 @@ class AccountValidator break; case TransactionTypeEnum::RECONCILIATION->value: - app('log')->debug('Calling validateReconciliationSource'); + Log::debug('Calling validateReconciliationSource'); $result = $this->validateReconciliationSource($array); break; @@ -216,17 +217,17 @@ class AccountValidator protected function canCreateTypes(array $accountTypes): bool { - app('log')->debug('Can we create any of these types?', $accountTypes); + Log::debug('Can we create any of these types?', $accountTypes); /** @var string $accountType */ foreach ($accountTypes as $accountType) { if ($this->canCreateType($accountType)) { - app('log')->debug(sprintf('YES, we can create a %s', $accountType)); + Log::debug(sprintf('YES, we can create a %s', $accountType)); return true; } } - app('log')->debug('NO, we cant create any of those.'); + Log::debug('NO, we cant create any of those.'); return false; } @@ -250,8 +251,8 @@ class AccountValidator */ protected function findExistingAccount(array $validTypes, array $data, bool $inverse = false): ?Account { - app('log')->debug('Now in findExistingAccount', [$validTypes, $data]); - app('log')->debug('The search will be reversed!'); + Log::debug('Now in findExistingAccount', [$validTypes, $data]); + Log::debug('The search will be reversed!'); $accountId = array_key_exists('id', $data) ? $data['id'] : null; $accountIban = array_key_exists('iban', $data) ? $data['iban'] : null; $accountNumber = array_key_exists('number', $data) ? $data['number'] : null; @@ -264,7 +265,7 @@ class AccountValidator $check = in_array($accountType, $validTypes, true); $check = $inverse ? !$check : $check; // reverse the validation check if necessary. if (($first instanceof Account) && $check) { - app('log')->debug(sprintf('ID: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban')); + Log::debug(sprintf('ID: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban')); return $first; } @@ -277,7 +278,7 @@ class AccountValidator $check = in_array($accountType, $validTypes, true); $check = $inverse ? !$check : $check; // reverse the validation check if necessary. if (($first instanceof Account) && $check) { - app('log')->debug(sprintf('Iban: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban')); + Log::debug(sprintf('Iban: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban')); return $first; } @@ -290,7 +291,7 @@ class AccountValidator $check = in_array($accountType, $validTypes, true); $check = $inverse ? !$check : $check; // reverse the validation check if necessary. if (($first instanceof Account) && $check) { - app('log')->debug(sprintf('Number: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban')); + Log::debug(sprintf('Number: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban')); return $first; } @@ -300,12 +301,12 @@ class AccountValidator if ('' !== (string) $accountName) { $first = $this->accountRepository->findByName($accountName, $validTypes); if ($first instanceof Account) { - app('log')->debug(sprintf('Name: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban')); + Log::debug(sprintf('Name: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban')); return $first; } } - app('log')->debug('Found nothing in findExistingAccount()'); + Log::debug('Found nothing in findExistingAccount()'); return null; } diff --git a/resources/lang/en_US/validation.php b/resources/lang/en_US/validation.php index 7c2134f5a2..f7b27c0da6 100644 --- a/resources/lang/en_US/validation.php +++ b/resources/lang/en_US/validation.php @@ -24,10 +24,10 @@ declare(strict_types=1); return [ - 'limit_exists' => 'There is already a budget limit (amount) for this budget and currency in the given period.', + 'limit_exists' => 'There is already a budget limit (amount) for this budget and currency in the given period.', 'invalid_sort_instruction' => 'The sort instruction is invalid for an object of type ":object".', - 'invalid_sort_instruction_index' => 'The sort instruction at index #:index is invalid for an object of type ":object".', - 'no_sort_instructions' => 'There are no sort instructions defined for an object of type ":object".', + 'invalid_sort_instruction_index' => 'The sort instruction at index #:index is invalid for an object of type ":object".', + 'no_sort_instructions' => 'There are no sort instructions defined for an object of type ":object".', 'webhook_budget_info' => 'Cannot deliver budget information for transaction related webhooks.', 'webhook_account_info' => 'Cannot deliver account information for budget related webhooks.', 'webhook_transaction_info' => 'Cannot deliver transaction information for budget related webhooks.', @@ -40,8 +40,8 @@ return [ 'nog_logged_in' => 'You are not logged in.', 'prohibited' => 'You must not submit anything in field.', 'bad_webhook_combination' => 'Webhook trigger ":trigger" cannot be combined with webhook response ":response".', - 'unknown_webhook_trigger' => 'Unknown webhook trigger ":trigger".', - 'only_any_trigger' => 'If you select the "Any event"-trigger, you may not select any other triggers.', + 'unknown_webhook_trigger' => 'Unknown webhook trigger ":trigger".', + 'only_any_trigger' => 'If you select the "Any event"-trigger, you may not select any other triggers.', 'bad_type_source' => 'Firefly III can\'t determine the transaction type based on this source account.', 'bad_type_destination' => 'Firefly III can\'t determine the transaction type based on this destination account.', 'missing_where' => 'Array is missing "where"-clause', @@ -123,7 +123,7 @@ return [ 'between.file' => 'The :attribute must be between :min and :max kilobytes.', 'between.string' => 'The :attribute must be between :min and :max characters.', 'between.array' => 'The :attribute must have between :min and :max items.', - 'between_date' => 'The date must be between the given start and end date.', + 'between_date' => 'The date must be between the given start and end date.', 'boolean' => 'The :attribute field must be true or false.', 'confirmed' => 'The :attribute confirmation does not match.', 'date' => 'The :attribute is not a valid date.', From 65813f290dcd9e4a5ed915f0a559e0e2313435c8 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 17 Sep 2025 13:48:56 +0200 Subject: [PATCH 18/91] Expand changelog. --- changelog.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/changelog.md b/changelog.md index 93c2a96ae5..aecba981e0 100644 --- a/changelog.md +++ b/changelog.md @@ -8,6 +8,15 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - Fixed a missing filter from [issue 10803](https://github.com/firefly-iii/firefly-iii/issues/10803). +- #10891 +- #10920 +- #10921 +- #10833 + +### API + +- #10908 + ## 6.4.0 - 2025-09-14 From dbf7dba421cfeee972f1e98779bdf4b80ad95f06 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 17 Sep 2025 20:04:24 +0200 Subject: [PATCH 19/91] Fix #10916 --- .../Correction/CorrectsAccountTypes.php | 77 +++++++++++++------ 1 file changed, 55 insertions(+), 22 deletions(-) diff --git a/app/Console/Commands/Correction/CorrectsAccountTypes.php b/app/Console/Commands/Correction/CorrectsAccountTypes.php index c56fc6f79a..dd0e83e889 100644 --- a/app/Console/Commands/Correction/CorrectsAccountTypes.php +++ b/app/Console/Commands/Correction/CorrectsAccountTypes.php @@ -29,12 +29,15 @@ use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Factory\AccountFactory; +use FireflyIII\Models\Account; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; use Illuminate\Console\Command; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Query\JoinClause; +use Illuminate\Support\Facades\Log; class CorrectsAccountTypes extends Command { @@ -45,6 +48,7 @@ class CorrectsAccountTypes extends Command private int $count; private array $expected; private AccountFactory $factory; + private AccountRepositoryInterface $repository; /** * Execute the console command. @@ -110,7 +114,7 @@ class CorrectsAccountTypes extends Command if ($resultSet->count() > 0) { $this->friendlyLine(sprintf('Found %d journals that need to be fixed.', $resultSet->count())); foreach ($resultSet as $entry) { - app('log')->debug(sprintf('Now fixing journal #%d', $entry->id)); + Log::debug(sprintf('Now fixing journal #%d', $entry->id)); /** @var null|TransactionJournal $journal */ $journal = TransactionJournal::find($entry->id); @@ -120,7 +124,7 @@ class CorrectsAccountTypes extends Command } } if (0 !== $this->count) { - app('log')->debug(sprintf('%d journals had to be fixed.', $this->count)); + Log::debug(sprintf('%d journals had to be fixed.', $this->count)); $this->friendlyInfo(sprintf('Acted on %d transaction(s)', $this->count)); } @@ -134,10 +138,10 @@ class CorrectsAccountTypes extends Command private function inspectJournal(TransactionJournal $journal): void { - app('log')->debug(sprintf('Now inspecting journal #%d', $journal->id)); + Log::debug(sprintf('Now inspecting journal #%d', $journal->id)); $transactions = $journal->transactions()->count(); if (2 !== $transactions) { - app('log')->debug(sprintf('Journal has %d transactions, so can\'t fix.', $transactions)); + Log::debug(sprintf('Journal has %d transactions, so can\'t fix.', $transactions)); $this->friendlyError(sprintf('Cannot inspect transaction journal #%d because it has %d transaction(s) instead of 2.', $journal->id, $transactions)); return; @@ -151,20 +155,20 @@ class CorrectsAccountTypes extends Command $destAccountType = $destAccount->accountType->type; if (!array_key_exists($type, $this->expected)) { - app('log')->info(sprintf('No source/destination info for transaction type %s.', $type)); + Log::info(sprintf('No source/destination info for transaction type %s.', $type)); $this->friendlyError(sprintf('No source/destination info for transaction type %s.', $type)); return; } if (!array_key_exists($sourceAccountType, $this->expected[$type])) { - app('log')->debug(sprintf('[a] Going to fix journal #%d', $journal->id)); + Log::debug(sprintf('[a] Going to fix journal #%d', $journal->id)); $this->fixJournal($journal, $type, $sourceTransaction, $destTransaction); return; } $expectedTypes = $this->expected[$type][$sourceAccountType]; if (!in_array($destAccountType, $expectedTypes, true)) { - app('log')->debug(sprintf('[b] Going to fix journal #%d', $journal->id)); + Log::debug(sprintf('[b] Going to fix journal #%d', $journal->id)); $this->fixJournal($journal, $type, $sourceTransaction, $destTransaction); } } @@ -181,13 +185,15 @@ class CorrectsAccountTypes extends Command private function fixJournal(TransactionJournal $journal, string $transactionType, Transaction $source, Transaction $dest): void { - app('log')->debug(sprintf('Going to fix journal #%d', $journal->id)); + Log::debug(sprintf('Going to fix journal #%d', $journal->id)); + $this->repository = app(AccountRepositoryInterface::class); + $this->repository->setUser($journal->user); ++$this->count; // variables: $sourceType = $source->account->accountType->type; $destinationType = $dest->account->accountType->type; $combination = sprintf('%s%s%s', $transactionType, $source->account->accountType->type, $dest->account->accountType->type); - app('log')->debug(sprintf('Combination is "%s"', $combination)); + Log::debug(sprintf('Combination is "%s"', $combination)); if ($this->shouldBeTransfer($transactionType, $sourceType, $destinationType)) { $this->makeTransfer($journal); @@ -220,10 +226,10 @@ class CorrectsAccountTypes extends Command return; } if (!$canCreateSource && !$hasValidSource) { - app('log')->debug('This transaction type has no source we can create. Just give error.'); + Log::debug('This transaction type has no source we can create. Just give error.'); $message = sprintf('The source account of %s #%d cannot be of type "%s". Firefly III cannot fix this. You may have to remove the transaction yourself.', $transactionType, $journal->id, $source->account->accountType->type); $this->friendlyError($message); - app('log')->debug($message); + Log::debug($message); return; } @@ -232,16 +238,24 @@ class CorrectsAccountTypes extends Command $validDestinations = $this->expected[$transactionType][$sourceType] ?? []; $canCreateDestination = $this->canCreateDestination($validDestinations); $hasValidDestination = $this->hasValidAccountType($validDestinations, $destinationType); + $alternativeDestination = $this->repository->findByName($dest->account->name, $validDestinations); if (!$hasValidDestination && $canCreateDestination) { $this->giveNewExpense($journal, $dest); return; } - if (!$canCreateDestination && !$hasValidDestination) { - app('log')->debug('This transaction type has no destination we can create. Just give error.'); + if (!$canCreateDestination && !$hasValidDestination && null === $alternativeDestination) { + Log::debug('This transaction type has no destination we can create. Just give error.'); $message = sprintf('The destination account of %s #%d cannot be of type "%s". Firefly III cannot fix this. You may have to remove the transaction yourself.', $transactionType, $journal->id, $dest->account->accountType->type); $this->friendlyError($message); - app('log')->debug($message); + Log::debug($message); + } + if (!$canCreateDestination && !$hasValidDestination && null !== $alternativeDestination) { + Log::debug('This transaction type has no destination we can create, but found alternative with the same name.'); + $message = sprintf('The destination account of %s #%d cannot be of type "%s". Firefly III found an alternative account. Please make sure this transaction is correct.', $transactionType, $journal->transaction_group_id, $dest->account->accountType->type); + $this->friendlyInfo($message); + Log::debug($message); + $this->giveNewDestinationAccount($journal, $alternativeDestination); } } @@ -263,7 +277,7 @@ class CorrectsAccountTypes extends Command $journal->save(); $message = sprintf('Converted transaction #%d from a transfer to a withdrawal.', $journal->id); $this->friendlyInfo($message); - app('log')->debug($message); + Log::debug($message); // check it again: $this->inspectJournal($journal); } @@ -281,7 +295,7 @@ class CorrectsAccountTypes extends Command $journal->save(); $message = sprintf('Converted transaction #%d from a transfer to a deposit.', $journal->id); $this->friendlyInfo($message); - app('log')->debug($message); + Log::debug($message); // check it again: $this->inspectJournal($journal); } @@ -308,7 +322,7 @@ class CorrectsAccountTypes extends Command $result->name ); $this->friendlyWarning($message); - app('log')->debug($message); + Log::debug($message); $this->inspectJournal($journal); } @@ -335,7 +349,7 @@ class CorrectsAccountTypes extends Command $result->name ); $this->friendlyWarning($message); - app('log')->debug($message); + Log::debug($message); $this->inspectJournal($journal); } @@ -354,14 +368,14 @@ class CorrectsAccountTypes extends Command private function giveNewRevenue(TransactionJournal $journal, Transaction $source): void { - app('log')->debug(sprintf('An account of type "%s" could be a valid source.', AccountTypeEnum::REVENUE->value)); + Log::debug(sprintf('An account of type "%s" could be a valid source.', AccountTypeEnum::REVENUE->value)); $this->factory->setUser($journal->user); $name = $source->account->name; $newSource = $this->factory->findOrCreate($name, AccountTypeEnum::REVENUE->value); $source->account()->associate($newSource); $source->save(); $this->friendlyPositive(sprintf('Firefly III gave transaction #%d a new source %s: #%d ("%s").', $journal->transaction_group_id, AccountTypeEnum::REVENUE->value, $newSource->id, $newSource->name)); - app('log')->debug(sprintf('Associated account #%d with transaction #%d', $newSource->id, $source->id)); + Log::debug(sprintf('Associated account #%d with transaction #%d', $newSource->id, $source->id)); $this->inspectJournal($journal); } @@ -372,14 +386,33 @@ class CorrectsAccountTypes extends Command private function giveNewExpense(TransactionJournal $journal, Transaction $destination): void { - app('log')->debug(sprintf('An account of type "%s" could be a valid destination.', AccountTypeEnum::EXPENSE->value)); + Log::debug(sprintf('An account of type "%s" could be a valid destination.', AccountTypeEnum::EXPENSE->value)); $this->factory->setUser($journal->user); $name = $destination->account->name; $newDestination = $this->factory->findOrCreate($name, AccountTypeEnum::EXPENSE->value); $destination->account()->associate($newDestination); $destination->save(); $this->friendlyPositive(sprintf('Firefly III gave transaction #%d a new destination %s: #%d ("%s").', $journal->transaction_group_id, AccountTypeEnum::EXPENSE->value, $newDestination->id, $newDestination->name)); - app('log')->debug(sprintf('Associated account #%d with transaction #%d', $newDestination->id, $destination->id)); + Log::debug(sprintf('Associated account #%d with transaction #%d', $newDestination->id, $destination->id)); $this->inspectJournal($journal); } + + private function giveNewDestinationAccount(TransactionJournal $journal, Account $newDestination): void + { + $destTransaction = $this->getDestinationTransaction($journal); + $oldDest = $destTransaction->account; + $destTransaction->account_id = $newDestination->id; + $destTransaction->save(); + $message = sprintf( + 'Transaction journal #%d, destination account changed from #%d ("%s") to #%d ("%s").', + $journal->id, + $oldDest->id, + $oldDest->name, + $newDestination->id, + $newDestination->name + ); + $this->friendlyInfo($message); + $journal->refresh(); + Log::debug($message); + } } From acc3c294d8bb9489aa382cc58044c241f152ca3e Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 17 Sep 2025 20:46:03 +0200 Subject: [PATCH 20/91] Fix #10924 --- resources/views/recurring/edit.twig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/views/recurring/edit.twig b/resources/views/recurring/edit.twig index 725337de76..37df0d26b6 100644 --- a/resources/views/recurring/edit.twig +++ b/resources/views/recurring/edit.twig @@ -134,9 +134,9 @@ {# BILL ONLY WHEN CREATING A WITHDRAWAL #} {% if bills|length > 1 %} - {{ ExpandedForm.select('bill_id', bills, array.transactions[0].bill_id) }} + {{ ExpandedForm.select('bill_id', bills, array.transactions[0].subscription_id) }} {% else %} - {{ ExpandedForm.select('bill_id', bills, array.transactions[0].bill_id, {helpText: trans('firefly.no_bill_pointer', {link: route('subscriptions.index')})}) }} + {{ ExpandedForm.select('bill_id', bills, array.transactions[0].subscription_id, {helpText: trans('firefly.no_bill_pointer', {link: route('subscriptions.index')})}) }} {% endif %} {# TAGS #} From 8a062983856b7613bc2642542185b774fd867c26 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 21 Sep 2025 07:35:46 +0200 Subject: [PATCH 21/91] Repair charts and balances. --- .../assets/v2/src/pages/dashboard/accounts.js | 15 +++++--------- .../v2/src/pages/dashboard/categories.js | 20 +++++++++++-------- .../partials/dashboard/account-list.blade.php | 12 +++-------- 3 files changed, 20 insertions(+), 27 deletions(-) diff --git a/resources/assets/v2/src/pages/dashboard/accounts.js b/resources/assets/v2/src/pages/dashboard/accounts.js index 5aab5eba4d..1361b0008c 100644 --- a/resources/assets/v2/src/pages/dashboard/accounts.js +++ b/resources/assets/v2/src/pages/dashboard/accounts.js @@ -211,14 +211,6 @@ export default () => ({ (new Get).show(accountId, new Date(window.store.get('end'))).then((response) => { let parent = response.data.data; - // apply function to each element of parent: - // parent.attributes.balances = parent.attributes.balances.map((balance) => { - // balance.amount_formatted = formatMoney(balance.amount, balance.currency_code); - // return balance; - // }); - // console.log(parent); - - // get groups for account: const params = { page: 1, @@ -261,11 +253,14 @@ export default () => ({ accounts.push({ name: parent.attributes.name, order: parent.attributes.order, + + current_balance: formatMoney(parent.attributes.current_balance, parent.attributes.currency_code), + pc_current_balance: null === parent.attributes.pc_current_balance ? null : formatMoney(parent.attributes.pc_current_balance, parent.attributes.primary_currency_code), + id: parent.id, - balances: parent.attributes.balances, + //balances: parent.attributes.balances, groups: groups, }); - // console.log(parent.attributes); count++; if (count === totalAccounts) { accounts.sort((a, b) => a.order - b.order); // b - a for reverse sort diff --git a/resources/assets/v2/src/pages/dashboard/categories.js b/resources/assets/v2/src/pages/dashboard/categories.js index d75da3acfb..9af9c42d75 100644 --- a/resources/assets/v2/src/pages/dashboard/categories.js +++ b/resources/assets/v2/src/pages/dashboard/categories.js @@ -64,13 +64,17 @@ export default () => ({ } } } - // loop data again to add amounts to each series. for (const i in data) { if (data.hasOwnProperty(i)) { let yAxis = 'y'; let current = data[i]; + + // allow switch to primary currency. let code = current.currency_code; + if(this.convertToPrimary) { + code = current.primary_currency_code; + } // loop series, add 0 if not present or add actual amount. for (const ii in series) { @@ -78,8 +82,11 @@ export default () => ({ let amount = 0.0; if (code === ii) { // this series' currency matches this column's currency. - amount = parseFloat(current.amount); - yAxis = 'y' + current.currency_code; + amount = parseFloat(current.entries.spent); + if(this.convertToPrimary) { + amount = parseFloat(current.entries.pc_entries.spent); + } + yAxis = 'y' + code; } if (series[ii].data.hasOwnProperty(current.label)) { // there is a value for this particular currency. The amount from this column will be added. @@ -103,7 +110,6 @@ export default () => ({ // loop the series and create ChartJS-compatible data sets. let count = 0; for (const i in series) { - // console.log('series'); let yAxisID = 'y' + i; let dataset = { label: i, @@ -148,16 +154,15 @@ export default () => ({ const end = new Date(window.store.get('end')); const cacheKey = getCacheKey('ds_ct_chart', {convertToPrimary: this.convertToPrimary, start: start, end: end}); - const cacheValid = window.store.get('cacheValid'); + // const cacheValid = window.store.get('cacheValid'); + const cacheValid = false; let cachedData = window.store.get(cacheKey); - if (cacheValid && typeof cachedData !== 'undefined') { chartData = cachedData; // save chart data for later. this.drawChart(this.generateOptions(chartData)); this.loading = false; return; } - const dashboard = new Dashboard(); dashboard.dashboard(start, end, null).then((response) => { chartData = response.data; // save chart data for later. @@ -181,7 +186,6 @@ export default () => ({ this.getFreshData(); }, init() { - // console.log('categories init'); Promise.all([getVariable('convert_to_primary', false),]).then((values) => { this.convertToPrimary = values[0]; afterPromises = true; diff --git a/resources/views/v2/partials/dashboard/account-list.blade.php b/resources/views/v2/partials/dashboard/account-list.blade.php index 9934551588..55170d2466 100644 --- a/resources/views/v2/partials/dashboard/account-list.blade.php +++ b/resources/views/v2/partials/dashboard/account-list.blade.php @@ -9,16 +9,10 @@

- - + - + () +

From 69cae3ae550706fda705876d49f8be7ef1ea8af7 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 21 Sep 2025 08:13:13 +0200 Subject: [PATCH 22/91] Fix autocomplete. --- .../assets/v2/src/pages/transactions/edit.js | 15 ++++++--------- .../pages/transactions/shared/load-currencies.js | 8 ++++---- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/resources/assets/v2/src/pages/transactions/edit.js b/resources/assets/v2/src/pages/transactions/edit.js index e95eca4ca4..cae648d243 100644 --- a/resources/assets/v2/src/pages/transactions/edit.js +++ b/resources/assets/v2/src/pages/transactions/edit.js @@ -72,8 +72,6 @@ let transactions = function () { resetButton: true, rulesButton: true, webhooksButton: true, - - }, // form behaviour during transaction @@ -85,7 +83,7 @@ let transactions = function () { // form data (except transactions) is stored in formData formData: { - defaultCurrency: null, + primaryCurrency: null, enabledCurrencies: [], primaryCurrencies: [], foreignCurrencies: [], @@ -200,8 +198,7 @@ let transactions = function () { // addedSplit, is called from the HTML // for source account const renderAccount = function (item, b, c) { - console.log(item); - return item.title + '
' + i18next.t('firefly.account_type_' + item.meta.type) + ''; + return item.name_with_balance + '
' + i18next.t('firefly.account_type_' + item.type) + ''; }; addAutocomplete({ selector: 'input.ac-source', @@ -209,7 +206,7 @@ let transactions = function () { account_types: this.filters.source, onRenderItem: renderAccount, valueField: 'id', - labelField: 'title', + labelField: 'name', onChange: changeSourceAccount, onSelectItem: selectSourceAccount }); @@ -217,7 +214,7 @@ let transactions = function () { selector: 'input.ac-dest', serverUrl: urls.account, valueField: 'id', - labelField: 'title', + labelField: 'name', account_types: this.filters.destination, onRenderItem: renderAccount, onChange: changeDestinationAccount, @@ -227,7 +224,7 @@ let transactions = function () { selector: 'input.ac-category', serverUrl: urls.category, valueField: 'id', - labelField: 'title', + labelField: 'name', onChange: changeCategory, onSelectItem: changeCategory }); @@ -330,7 +327,7 @@ let transactions = function () { // load meta data. loadCurrencies().then(data => { this.formStates.loadingCurrencies = false; - this.formData.defaultCurrency = data.defaultCurrency; + this.formData.primaryCurrency = data.primaryCurrency; this.formData.enabledCurrencies = data.enabledCurrencies; this.formData.primaryCurrencies = data.primaryCurrencies; this.formData.foreignCurrencies = data.foreignCurrencies; diff --git a/resources/assets/v2/src/pages/transactions/shared/load-currencies.js b/resources/assets/v2/src/pages/transactions/shared/load-currencies.js index 11c05baf67..2580a1389b 100644 --- a/resources/assets/v2/src/pages/transactions/shared/load-currencies.js +++ b/resources/assets/v2/src/pages/transactions/shared/load-currencies.js @@ -28,7 +28,7 @@ export function loadCurrencies() { let getter = new Get(); return getter.list(params).then((response) => { let returnData = { - defaultCurrency: {}, + primaryCurrency: {}, primaryCurrencies: [], foreignCurrencies: [], enabledCurrencies: [], @@ -46,13 +46,13 @@ export function loadCurrencies() { id: current.id, name: current.attributes.name, code: current.attributes.code, - default: current.attributes.default, + primary: current.attributes.primary, symbol: current.attributes.symbol, decimal_places: current.attributes.decimal_places, }; - if (obj.default) { - returnData.defaultCurrency = obj; + if (obj.primary) { + returnData.primaryCurrency = obj; } returnData.enabledCurrencies.push(obj); returnData.primaryCurrencies.push(obj); From 8c0ee8f024bbc690995274de307fda90752c5ead Mon Sep 17 00:00:00 2001 From: JC5 Date: Sun, 21 Sep 2025 08:28:09 +0200 Subject: [PATCH 23/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-09-21?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Correction/CorrectsAccountTypes.php | 28 +- app/Models/CurrencyExchangeRate.php | 2 +- .../Internal/Support/JournalServiceTrait.php | 12 +- app/Validation/Account/DepositValidation.php | 2 +- composer.lock | 74 +-- config/firefly.php | 4 +- package-lock.json | 433 +++++++++--------- resources/assets/v1/src/locales/cs.json | 2 +- resources/assets/v1/src/locales/it.json | 22 +- 9 files changed, 299 insertions(+), 280 deletions(-) diff --git a/app/Console/Commands/Correction/CorrectsAccountTypes.php b/app/Console/Commands/Correction/CorrectsAccountTypes.php index dd0e83e889..f554197c18 100644 --- a/app/Console/Commands/Correction/CorrectsAccountTypes.php +++ b/app/Console/Commands/Correction/CorrectsAccountTypes.php @@ -186,13 +186,13 @@ class CorrectsAccountTypes extends Command private function fixJournal(TransactionJournal $journal, string $transactionType, Transaction $source, Transaction $dest): void { Log::debug(sprintf('Going to fix journal #%d', $journal->id)); - $this->repository = app(AccountRepositoryInterface::class); + $this->repository = app(AccountRepositoryInterface::class); $this->repository->setUser($journal->user); ++$this->count; // variables: - $sourceType = $source->account->accountType->type; - $destinationType = $dest->account->accountType->type; - $combination = sprintf('%s%s%s', $transactionType, $source->account->accountType->type, $dest->account->accountType->type); + $sourceType = $source->account->accountType->type; + $destinationType = $dest->account->accountType->type; + $combination = sprintf('%s%s%s', $transactionType, $source->account->accountType->type, $dest->account->accountType->type); Log::debug(sprintf('Combination is "%s"', $combination)); if ($this->shouldBeTransfer($transactionType, $sourceType, $destinationType)) { @@ -217,9 +217,9 @@ class CorrectsAccountTypes extends Command } // transaction has no valid source. - $validSources = array_keys($this->expected[$transactionType]); - $canCreateSource = $this->canCreateSource($validSources); - $hasValidSource = $this->hasValidAccountType($validSources, $sourceType); + $validSources = array_keys($this->expected[$transactionType]); + $canCreateSource = $this->canCreateSource($validSources); + $hasValidSource = $this->hasValidAccountType($validSources, $sourceType); if (!$hasValidSource && $canCreateSource) { $this->giveNewRevenue($journal, $source); @@ -235,9 +235,9 @@ class CorrectsAccountTypes extends Command } /** @var array $validDestinations */ - $validDestinations = $this->expected[$transactionType][$sourceType] ?? []; - $canCreateDestination = $this->canCreateDestination($validDestinations); - $hasValidDestination = $this->hasValidAccountType($validDestinations, $destinationType); + $validDestinations = $this->expected[$transactionType][$sourceType] ?? []; + $canCreateDestination = $this->canCreateDestination($validDestinations); + $hasValidDestination = $this->hasValidAccountType($validDestinations, $destinationType); $alternativeDestination = $this->repository->findByName($dest->account->name, $validDestinations); if (!$hasValidDestination && $canCreateDestination) { $this->giveNewExpense($journal, $dest); @@ -255,7 +255,7 @@ class CorrectsAccountTypes extends Command $message = sprintf('The destination account of %s #%d cannot be of type "%s". Firefly III found an alternative account. Please make sure this transaction is correct.', $transactionType, $journal->transaction_group_id, $dest->account->accountType->type); $this->friendlyInfo($message); Log::debug($message); - $this->giveNewDestinationAccount($journal, $alternativeDestination); + $this->giveNewDestinationAccount($journal, $alternativeDestination); } } @@ -399,11 +399,11 @@ class CorrectsAccountTypes extends Command private function giveNewDestinationAccount(TransactionJournal $journal, Account $newDestination): void { - $destTransaction = $this->getDestinationTransaction($journal); - $oldDest = $destTransaction->account; + $destTransaction = $this->getDestinationTransaction($journal); + $oldDest = $destTransaction->account; $destTransaction->account_id = $newDestination->id; $destTransaction->save(); - $message = sprintf( + $message = sprintf( 'Transaction journal #%d, destination account changed from #%d ("%s") to #%d ("%s").', $journal->id, $oldDest->id, diff --git a/app/Models/CurrencyExchangeRate.php b/app/Models/CurrencyExchangeRate.php index 7edec3b797..a1bfa4a013 100644 --- a/app/Models/CurrencyExchangeRate.php +++ b/app/Models/CurrencyExchangeRate.php @@ -38,7 +38,7 @@ class CurrencyExchangeRate extends Model use ReturnsIntegerUserIdTrait; use SoftDeletes; - protected $fillable = ['user_id','user_group_id', 'from_currency_id', 'to_currency_id', 'date', 'date_tz', 'rate']; + protected $fillable = ['user_id', 'user_group_id', 'from_currency_id', 'to_currency_id', 'date', 'date_tz', 'rate']; public function fromCurrency(): BelongsTo { diff --git a/app/Services/Internal/Support/JournalServiceTrait.php b/app/Services/Internal/Support/JournalServiceTrait.php index 9d7cedad90..8cd15203ca 100644 --- a/app/Services/Internal/Support/JournalServiceTrait.php +++ b/app/Services/Internal/Support/JournalServiceTrait.php @@ -123,8 +123,9 @@ trait JournalServiceTrait if (null !== $search && in_array($search->accountType->type, $types, true)) { Log::debug(sprintf('Found "account_id" object: #%d, "%s" of type %s (1)', $search->id, $search->name, $search->accountType->type)); - if($opposite?->id === $search->id) { + if ($opposite?->id === $search->id) { Log::debug(sprintf('Account #%d is the same as opposite account #%d, returning NULL.', $search->id, $opposite->id)); + return null; } @@ -163,8 +164,9 @@ trait JournalServiceTrait if (null !== $result) { Log::debug(sprintf('Found "account_iban" object: #%d, %s', $result->id, $result->name)); - if($opposite?->id === $result->id) { + if ($opposite?->id === $result->id) { Log::debug(sprintf('Account #%d is the same as opposite account #%d, returning NULL.', $result->id, $opposite->id)); + return null; } @@ -196,8 +198,9 @@ trait JournalServiceTrait if (null !== $result) { Log::debug(sprintf('Found account: #%d, %s', $result->id, $result->name)); - if($opposite?->id === $result->id) { + if ($opposite?->id === $result->id) { Log::debug(sprintf('Account #%d is the same as opposite account #%d, returning NULL.', $result->id, $opposite->id)); + return null; } @@ -231,8 +234,9 @@ trait JournalServiceTrait if (null !== $result) { Log::debug(sprintf('Found "account_name" object: #%d, %s', $result->id, $result->name)); - if($opposite?->id === $result->id) { + if ($opposite?->id === $result->id) { Log::debug(sprintf('Account #%d is the same as opposite account #%d, returning NULL.', $result->id, $opposite->id)); + return null; } diff --git a/app/Validation/Account/DepositValidation.php b/app/Validation/Account/DepositValidation.php index 42a38447b6..36cd7e7d35 100644 --- a/app/Validation/Account/DepositValidation.php +++ b/app/Validation/Account/DepositValidation.php @@ -133,7 +133,7 @@ trait DepositValidation Log::debug(sprintf('Firefly III does not accept ID #%d as valid account data.', $accountId)); // #10921 Set result false $this->sourceError = (string) trans('validation.withdrawal_source_bad_data', ['id' => $accountId, 'name' => $accountName]); - $result = false; + $result = false; } if (null !== $search && in_array($search->accountType->type, $validTypes, true)) { Log::debug('ID result is not null and seems valid, save as source account.'); diff --git a/composer.lock b/composer.lock index ac2158c967..dce4114021 100644 --- a/composer.lock +++ b/composer.lock @@ -1878,16 +1878,16 @@ }, { "name": "laravel/framework", - "version": "v12.29.0", + "version": "v12.30.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "a9e4c73086f5ba38383e9c1d74b84fe46aac730b" + "reference": "7f61e8679f9142f282a0184ac7ef9e3834bfd023" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/a9e4c73086f5ba38383e9c1d74b84fe46aac730b", - "reference": "a9e4c73086f5ba38383e9c1d74b84fe46aac730b", + "url": "https://api.github.com/repos/laravel/framework/zipball/7f61e8679f9142f282a0184ac7ef9e3834bfd023", + "reference": "7f61e8679f9142f282a0184ac7ef9e3834bfd023", "shasum": "" }, "require": { @@ -1915,7 +1915,7 @@ "monolog/monolog": "^3.0", "nesbot/carbon": "^3.8.4", "nunomaduro/termwind": "^2.0", - "phiki/phiki": "v2.0.0", + "phiki/phiki": "^2.0.0", "php": "^8.2", "psr/container": "^1.1.1|^2.0.1", "psr/log": "^1.0|^2.0|^3.0", @@ -2094,7 +2094,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-09-16T14:15:03+00:00" + "time": "2025-09-18T21:07:07+00:00" }, { "name": "laravel/passport", @@ -4352,16 +4352,16 @@ }, { "name": "phiki/phiki", - "version": "v2.0.0", + "version": "v2.0.3", "source": { "type": "git", "url": "https://github.com/phikiphp/phiki.git", - "reference": "461f6dd7e91dc3a95463b42f549ac7d0aab4702f" + "reference": "fe51fe6dc31856cd776fd1b04ee74053a4271644" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phikiphp/phiki/zipball/461f6dd7e91dc3a95463b42f549ac7d0aab4702f", - "reference": "461f6dd7e91dc3a95463b42f549ac7d0aab4702f", + "url": "https://api.github.com/repos/phikiphp/phiki/zipball/fe51fe6dc31856cd776fd1b04ee74053a4271644", + "reference": "fe51fe6dc31856cd776fd1b04ee74053a4271644", "shasum": "" }, "require": { @@ -4407,7 +4407,7 @@ "description": "Syntax highlighting using TextMate grammars in PHP.", "support": { "issues": "https://github.com/phikiphp/phiki/issues", - "source": "https://github.com/phikiphp/phiki/tree/v2.0.0" + "source": "https://github.com/phikiphp/phiki/tree/v2.0.3" }, "funding": [ { @@ -4419,7 +4419,7 @@ "type": "other" } ], - "time": "2025-08-28T18:20:27+00:00" + "time": "2025-09-19T11:50:41+00:00" }, { "name": "php-http/client-common", @@ -5045,21 +5045,21 @@ }, { "name": "pragmarx/google2fa-qrcode", - "version": "v3.0.0", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/antonioribeiro/google2fa-qrcode.git", - "reference": "ce4d8a729b6c93741c607cfb2217acfffb5bf76b" + "reference": "c23ebcc3a50de0d1566016a6dd1486e183bb78e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antonioribeiro/google2fa-qrcode/zipball/ce4d8a729b6c93741c607cfb2217acfffb5bf76b", - "reference": "ce4d8a729b6c93741c607cfb2217acfffb5bf76b", + "url": "https://api.github.com/repos/antonioribeiro/google2fa-qrcode/zipball/c23ebcc3a50de0d1566016a6dd1486e183bb78e1", + "reference": "c23ebcc3a50de0d1566016a6dd1486e183bb78e1", "shasum": "" }, "require": { "php": ">=7.1", - "pragmarx/google2fa": ">=4.0" + "pragmarx/google2fa": "^4.0|^5.0|^6.0|^7.0|^8.0" }, "require-dev": { "bacon/bacon-qr-code": "^2.0", @@ -5106,9 +5106,9 @@ ], "support": { "issues": "https://github.com/antonioribeiro/google2fa-qrcode/issues", - "source": "https://github.com/antonioribeiro/google2fa-qrcode/tree/v3.0.0" + "source": "https://github.com/antonioribeiro/google2fa-qrcode/tree/v3.0.1" }, - "time": "2021-08-15T12:53:48+00:00" + "time": "2025-09-19T23:02:26+00:00" }, { "name": "pragmarx/random", @@ -10810,16 +10810,16 @@ }, { "name": "larastan/larastan", - "version": "v3.7.1", + "version": "v3.7.2", "source": { "type": "git", "url": "https://github.com/larastan/larastan.git", - "reference": "2e653fd19585a825e283b42f38378b21ae481cc7" + "reference": "a761859a7487bd7d0cb8b662a7538a234d5bb5ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/larastan/larastan/zipball/2e653fd19585a825e283b42f38378b21ae481cc7", - "reference": "2e653fd19585a825e283b42f38378b21ae481cc7", + "url": "https://api.github.com/repos/larastan/larastan/zipball/a761859a7487bd7d0cb8b662a7538a234d5bb5ae", + "reference": "a761859a7487bd7d0cb8b662a7538a234d5bb5ae", "shasum": "" }, "require": { @@ -10833,7 +10833,7 @@ "illuminate/pipeline": "^11.44.2 || ^12.4.1", "illuminate/support": "^11.44.2 || ^12.4.1", "php": "^8.2", - "phpstan/phpstan": "^2.1.23" + "phpstan/phpstan": "^2.1.28" }, "require-dev": { "doctrine/coding-standard": "^13", @@ -10887,7 +10887,7 @@ ], "support": { "issues": "https://github.com/larastan/larastan/issues", - "source": "https://github.com/larastan/larastan/tree/v3.7.1" + "source": "https://github.com/larastan/larastan/tree/v3.7.2" }, "funding": [ { @@ -10895,7 +10895,7 @@ "type": "github" } ], - "time": "2025-09-10T19:42:11+00:00" + "time": "2025-09-19T09:03:05+00:00" }, { "name": "laravel-json-api/testing", @@ -11404,16 +11404,16 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.26", + "version": "2.1.28", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "b13345001a8553ec405b7741be0c6b8d7f8b5bf5" + "reference": "578fa296a166605d97b94091f724f1257185d278" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b13345001a8553ec405b7741be0c6b8d7f8b5bf5", - "reference": "b13345001a8553ec405b7741be0c6b8d7f8b5bf5", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/578fa296a166605d97b94091f724f1257185d278", + "reference": "578fa296a166605d97b94091f724f1257185d278", "shasum": "" }, "require": { @@ -11458,7 +11458,7 @@ "type": "github" } ], - "time": "2025-09-16T11:33:46+00:00" + "time": "2025-09-19T08:58:49+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -11557,16 +11557,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "12.3.7", + "version": "12.3.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "bbede0f5593dad37af3be6a6f8e6ae1885e8a0a9" + "reference": "99e692c6a84708211f7536ba322bbbaef57ac7fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/bbede0f5593dad37af3be6a6f8e6ae1885e8a0a9", - "reference": "bbede0f5593dad37af3be6a6f8e6ae1885e8a0a9", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/99e692c6a84708211f7536ba322bbbaef57ac7fc", + "reference": "99e692c6a84708211f7536ba322bbbaef57ac7fc", "shasum": "" }, "require": { @@ -11622,7 +11622,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.3.7" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.3.8" }, "funding": [ { @@ -11642,7 +11642,7 @@ "type": "tidelift" } ], - "time": "2025-09-10T09:59:06+00:00" + "time": "2025-09-17T11:31:43+00:00" }, { "name": "phpunit/php-file-iterator", diff --git a/config/firefly.php b/config/firefly.php index d5c69546c3..6bbe1f0ea2 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -78,8 +78,8 @@ return [ 'running_balance_column' => env('USE_RUNNING_BALANCE', false), // see cer.php for exchange rates feature flag. ], - 'version' => 'develop/2025-09-16', - 'build_time' => 1758048808, + 'version' => 'develop/2025-09-21', + 'build_time' => 1758435980, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 26, diff --git a/package-lock.json b/package-lock.json index 671dd29016..a12d8faaf8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1693,9 +1693,9 @@ "license": "MIT" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", - "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", "cpu": [ "ppc64" ], @@ -1710,9 +1710,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", - "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", "cpu": [ "arm" ], @@ -1727,9 +1727,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", - "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", "cpu": [ "arm64" ], @@ -1744,9 +1744,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", - "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", "cpu": [ "x64" ], @@ -1761,9 +1761,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", - "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", "cpu": [ "arm64" ], @@ -1778,9 +1778,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", - "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", "cpu": [ "x64" ], @@ -1795,9 +1795,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", - "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", "cpu": [ "arm64" ], @@ -1812,9 +1812,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", - "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", "cpu": [ "x64" ], @@ -1829,9 +1829,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", - "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", "cpu": [ "arm" ], @@ -1846,9 +1846,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", - "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", "cpu": [ "arm64" ], @@ -1863,9 +1863,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", - "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", "cpu": [ "ia32" ], @@ -1880,9 +1880,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", - "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", "cpu": [ "loong64" ], @@ -1897,9 +1897,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", - "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", "cpu": [ "mips64el" ], @@ -1914,9 +1914,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", - "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", "cpu": [ "ppc64" ], @@ -1931,9 +1931,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", - "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", "cpu": [ "riscv64" ], @@ -1948,9 +1948,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", - "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", "cpu": [ "s390x" ], @@ -1965,9 +1965,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", - "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", "cpu": [ "x64" ], @@ -1982,9 +1982,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", - "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", "cpu": [ "arm64" ], @@ -1999,9 +1999,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", - "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", "cpu": [ "x64" ], @@ -2016,9 +2016,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", - "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", "cpu": [ "arm64" ], @@ -2033,9 +2033,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", - "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", "cpu": [ "x64" ], @@ -2050,9 +2050,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", - "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", "cpu": [ "arm64" ], @@ -2067,9 +2067,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", - "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", "cpu": [ "x64" ], @@ -2084,9 +2084,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", - "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", "cpu": [ "arm64" ], @@ -2101,9 +2101,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", - "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", "cpu": [ "ia32" ], @@ -2118,9 +2118,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", - "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", "cpu": [ "x64" ], @@ -2589,9 +2589,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.2.tgz", - "integrity": "sha512-uLN8NAiFVIRKX9ZQha8wy6UUs06UNSZ32xj6giK/rmMXAgKahwExvK6SsmgU5/brh4w/nSgj8e0k3c1HBQpa0A==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.0.tgz", + "integrity": "sha512-VxDYCDqOaR7NXzAtvRx7G1u54d2kEHopb28YH/pKzY6y0qmogP3gG7CSiWsq9WvDFxOQMpNEyjVAHZFXfH3o/A==", "cpu": [ "arm" ], @@ -2603,9 +2603,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.2.tgz", - "integrity": "sha512-oEouqQk2/zxxj22PNcGSskya+3kV0ZKH+nQxuCCOGJ4oTXBdNTbv+f/E3c74cNLeMO1S5wVWacSws10TTSB77g==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.0.tgz", + "integrity": "sha512-pqDirm8koABIKvzL59YI9W9DWbRlTX7RWhN+auR8HXJxo89m4mjqbah7nJZjeKNTNYopqL+yGg+0mhCpf3xZtQ==", "cpu": [ "arm64" ], @@ -2617,9 +2617,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.2.tgz", - "integrity": "sha512-OZuTVTpj3CDSIxmPgGH8en/XtirV5nfljHZ3wrNwvgkT5DQLhIKAeuFSiwtbMto6oVexV0k1F1zqURPKf5rI1Q==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.0.tgz", + "integrity": "sha512-YCdWlY/8ltN6H78HnMsRHYlPiKvqKagBP1r+D7SSylxX+HnsgXGCmLiV3Y4nSyY9hW8qr8U9LDUx/Lo7M6MfmQ==", "cpu": [ "arm64" ], @@ -2631,9 +2631,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.2.tgz", - "integrity": "sha512-Wa/Wn8RFkIkr1vy1k1PB//VYhLnlnn5eaJkfTQKivirOvzu5uVd2It01ukeQstMursuz7S1bU+8WW+1UPXpa8A==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.0.tgz", + "integrity": "sha512-z4nw6y1j+OOSGzuVbSWdIp1IUks9qNw4dc7z7lWuWDKojY38VMWBlEN7F9jk5UXOkUcp97vA1N213DF+Lz8BRg==", "cpu": [ "x64" ], @@ -2645,9 +2645,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.2.tgz", - "integrity": "sha512-QkzxvH3kYN9J1w7D1A+yIMdI1pPekD+pWx7G5rXgnIlQ1TVYVC6hLl7SOV9pi5q9uIDF9AuIGkuzcbF7+fAhow==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.0.tgz", + "integrity": "sha512-Q/dv9Yvyr5rKlK8WQJZVrp5g2SOYeZUs9u/t2f9cQ2E0gJjYB/BWoedXfUT0EcDJefi2zzVfhcOj8drWCzTviw==", "cpu": [ "arm64" ], @@ -2659,9 +2659,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.2.tgz", - "integrity": "sha512-dkYXB0c2XAS3a3jmyDkX4Jk0m7gWLFzq1C3qUnJJ38AyxIF5G/dyS4N9B30nvFseCfgtCEdbYFhk0ChoCGxPog==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.0.tgz", + "integrity": "sha512-kdBsLs4Uile/fbjZVvCRcKB4q64R+1mUq0Yd7oU1CMm1Av336ajIFqNFovByipciuUQjBCPMxwJhCgfG2re3rg==", "cpu": [ "x64" ], @@ -2673,9 +2673,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.2.tgz", - "integrity": "sha512-9VlPY/BN3AgbukfVHAB8zNFWB/lKEuvzRo1NKev0Po8sYFKx0i+AQlCYftgEjcL43F2h9Ui1ZSdVBc4En/sP2w==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.0.tgz", + "integrity": "sha512-aL6hRwu0k7MTUESgkg7QHY6CoqPgr6gdQXRJI1/VbFlUMwsSzPGSR7sG5d+MCbYnJmJwThc2ol3nixj1fvI/zQ==", "cpu": [ "arm" ], @@ -2687,9 +2687,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.2.tgz", - "integrity": "sha512-+GdKWOvsifaYNlIVf07QYan1J5F141+vGm5/Y8b9uCZnG/nxoGqgCmR24mv0koIWWuqvFYnbURRqw1lv7IBINw==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.0.tgz", + "integrity": "sha512-BTs0M5s1EJejgIBJhCeiFo7GZZ2IXWkFGcyZhxX4+8usnIo5Mti57108vjXFIQmmJaRyDwmV59Tw64Ap1dkwMw==", "cpu": [ "arm" ], @@ -2701,9 +2701,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.2.tgz", - "integrity": "sha512-df0Eou14ojtUdLQdPFnymEQteENwSJAdLf5KCDrmZNsy1c3YaCNaJvYsEUHnrg+/DLBH612/R0xd3dD03uz2dg==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.0.tgz", + "integrity": "sha512-uj672IVOU9m08DBGvoPKPi/J8jlVgjh12C9GmjjBxCTQc3XtVmRkRKyeHSmIKQpvJ7fIm1EJieBUcnGSzDVFyw==", "cpu": [ "arm64" ], @@ -2715,9 +2715,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.2.tgz", - "integrity": "sha512-iPeouV0UIDtz8j1YFR4OJ/zf7evjauqv7jQ/EFs0ClIyL+by++hiaDAfFipjOgyz6y6xbDvJuiU4HwpVMpRFDQ==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.0.tgz", + "integrity": "sha512-/+IVbeDMDCtB/HP/wiWsSzduD10SEGzIZX2945KSgZRNi4TSkjHqRJtNTVtVb8IRwhJ65ssI56krlLik+zFWkw==", "cpu": [ "arm64" ], @@ -2729,9 +2729,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.50.2.tgz", - "integrity": "sha512-OL6KaNvBopLlj5fTa5D5bau4W82f+1TyTZRr2BdnfsrnQnmdxh4okMxR2DcDkJuh4KeoQZVuvHvzuD/lyLn2Kw==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.0.tgz", + "integrity": "sha512-U1vVzvSWtSMWKKrGoROPBXMh3Vwn93TA9V35PldokHGqiUbF6erSzox/5qrSMKp6SzakvyjcPiVF8yB1xKr9Pg==", "cpu": [ "loong64" ], @@ -2743,9 +2743,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.2.tgz", - "integrity": "sha512-I21VJl1w6z/K5OTRl6aS9DDsqezEZ/yKpbqlvfHbW0CEF5IL8ATBMuUx6/mp683rKTK8thjs/0BaNrZLXetLag==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.0.tgz", + "integrity": "sha512-X/4WfuBAdQRH8cK3DYl8zC00XEE6aM472W+QCycpQJeLWVnHfkv7RyBFVaTqNUMsTgIX8ihMjCvFF9OUgeABzw==", "cpu": [ "ppc64" ], @@ -2757,9 +2757,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.2.tgz", - "integrity": "sha512-Hq6aQJT/qFFHrYMjS20nV+9SKrXL2lvFBENZoKfoTH2kKDOJqff5OSJr4x72ZaG/uUn+XmBnGhfr4lwMRrmqCQ==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.0.tgz", + "integrity": "sha512-xIRYc58HfWDBZoLmWfWXg2Sq8VCa2iJ32B7mqfWnkx5mekekl0tMe7FHpY8I72RXEcUkaWawRvl3qA55og+cwQ==", "cpu": [ "riscv64" ], @@ -2771,9 +2771,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.2.tgz", - "integrity": "sha512-82rBSEXRv5qtKyr0xZ/YMF531oj2AIpLZkeNYxmKNN6I2sVE9PGegN99tYDLK2fYHJITL1P2Lgb4ZXnv0PjQvw==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.0.tgz", + "integrity": "sha512-mbsoUey05WJIOz8U1WzNdf+6UMYGwE3fZZnQqsM22FZ3wh1N887HT6jAOjXs6CNEK3Ntu2OBsyQDXfIjouI4dw==", "cpu": [ "riscv64" ], @@ -2785,9 +2785,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.2.tgz", - "integrity": "sha512-4Q3S3Hy7pC6uaRo9gtXUTJ+EKo9AKs3BXKc2jYypEcMQ49gDPFU2P1ariX9SEtBzE5egIX6fSUmbmGazwBVF9w==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.0.tgz", + "integrity": "sha512-qP6aP970bucEi5KKKR4AuPFd8aTx9EF6BvutvYxmZuWLJHmnq4LvBfp0U+yFDMGwJ+AIJEH5sIP+SNypauMWzg==", "cpu": [ "s390x" ], @@ -2799,9 +2799,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.2.tgz", - "integrity": "sha512-9Jie/At6qk70dNIcopcL4p+1UirusEtznpNtcq/u/C5cC4HBX7qSGsYIcG6bdxj15EYWhHiu02YvmdPzylIZlA==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.0.tgz", + "integrity": "sha512-nmSVN+F2i1yKZ7rJNKO3G7ZzmxJgoQBQZ/6c4MuS553Grmr7WqR7LLDcYG53Z2m9409z3JLt4sCOhLdbKQ3HmA==", "cpu": [ "x64" ], @@ -2813,9 +2813,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.2.tgz", - "integrity": "sha512-HPNJwxPL3EmhzeAnsWQCM3DcoqOz3/IC6de9rWfGR8ZCuEHETi9km66bH/wG3YH0V3nyzyFEGUZeL5PKyy4xvw==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.0.tgz", + "integrity": "sha512-2d0qRo33G6TfQVjaMR71P+yJVGODrt5V6+T0BDYH4EMfGgdC/2HWDVjSSFw888GSzAZUwuska3+zxNUCDco6rQ==", "cpu": [ "x64" ], @@ -2827,9 +2827,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.2.tgz", - "integrity": "sha512-nMKvq6FRHSzYfKLHZ+cChowlEkR2lj/V0jYj9JnGUVPL2/mIeFGmVM2mLaFeNa5Jev7W7TovXqXIG2d39y1KYA==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.0.tgz", + "integrity": "sha512-A1JalX4MOaFAAyGgpO7XP5khquv/7xKzLIyLmhNrbiCxWpMlnsTYr8dnsWM7sEeotNmxvSOEL7F65j0HXFcFsw==", "cpu": [ "arm64" ], @@ -2841,9 +2841,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.2.tgz", - "integrity": "sha512-eFUvvnTYEKeTyHEijQKz81bLrUQOXKZqECeiWH6tb8eXXbZk+CXSG2aFrig2BQ/pjiVRj36zysjgILkqarS2YA==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.0.tgz", + "integrity": "sha512-YQugafP/rH0eOOHGjmNgDURrpYHrIX0yuojOI8bwCyXwxC9ZdTd3vYkmddPX0oHONLXu9Rb1dDmT0VNpjkzGGw==", "cpu": [ "arm64" ], @@ -2855,9 +2855,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.2.tgz", - "integrity": "sha512-cBaWmXqyfRhH8zmUxK3d3sAhEWLrtMjWBRwdMMHJIXSjvjLKvv49adxiEz+FJ8AP90apSDDBx2Tyd/WylV6ikA==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.0.tgz", + "integrity": "sha512-zYdUYhi3Qe2fndujBqL5FjAFzvNeLxtIqfzNEVKD1I7C37/chv1VxhscWSQHTNfjPCrBFQMnynwA3kpZpZ8w4A==", "cpu": [ "ia32" ], @@ -2868,10 +2868,24 @@ "win32" ] }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.0.tgz", + "integrity": "sha512-fGk03kQylNaCOQ96HDMeT7E2n91EqvCDd3RwvT5k+xNdFCeMGnj5b5hEgTGrQuyidqSsD3zJDQ21QIaxXqTBJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.2.tgz", - "integrity": "sha512-APwKy6YUhvZaEoHyM+9xqmTpviEI+9eL7LoCH+aLcvWYHJ663qG5zx7WzWZY+a9qkg5JtzcMyJ9z0WtQBMDmgA==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.0.tgz", + "integrity": "sha512-6iKDCVSIUQ8jPMoIV0OytRKniaYyy5EbY/RRydmLW8ZR3cEBhxbWl5ro0rkUNe0ef6sScvhbY79HrjRm8i3vDQ==", "cpu": [ "x64" ], @@ -3159,9 +3173,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.0.tgz", - "integrity": "sha512-y1dMvuvJspJiPSDZUQ+WMBvF7dpnEqN4x9DDC9ie5Fs/HUZJA3wFp7EhHoVaKX/iI0cRoECV8X2jL8zi0xrHCg==", + "version": "24.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.2.tgz", + "integrity": "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4061,9 +4075,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.4.tgz", - "integrity": "sha512-L+YvJwGAgwJBV1p6ffpSTa2KRc69EeeYGYjRVWKs0GKrK+LON0GC0gV+rKSNtALEDvMDqkvCFq9r1r94/Gjwxw==", + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.6.tgz", + "integrity": "sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5722,9 +5736,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.218", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.218.tgz", - "integrity": "sha512-uwwdN0TUHs8u6iRgN8vKeWZMRll4gBkz+QMqdS7DDe49uiK68/UX92lFb61oiFPrpYZNeZIqa4bA7O6Aiasnzg==", + "version": "1.5.222", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.222.tgz", + "integrity": "sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w==", "dev": true, "license": "ISC" }, @@ -5885,9 +5899,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", - "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -5898,32 +5912,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.9", - "@esbuild/android-arm": "0.25.9", - "@esbuild/android-arm64": "0.25.9", - "@esbuild/android-x64": "0.25.9", - "@esbuild/darwin-arm64": "0.25.9", - "@esbuild/darwin-x64": "0.25.9", - "@esbuild/freebsd-arm64": "0.25.9", - "@esbuild/freebsd-x64": "0.25.9", - "@esbuild/linux-arm": "0.25.9", - "@esbuild/linux-arm64": "0.25.9", - "@esbuild/linux-ia32": "0.25.9", - "@esbuild/linux-loong64": "0.25.9", - "@esbuild/linux-mips64el": "0.25.9", - "@esbuild/linux-ppc64": "0.25.9", - "@esbuild/linux-riscv64": "0.25.9", - "@esbuild/linux-s390x": "0.25.9", - "@esbuild/linux-x64": "0.25.9", - "@esbuild/netbsd-arm64": "0.25.9", - "@esbuild/netbsd-x64": "0.25.9", - "@esbuild/openbsd-arm64": "0.25.9", - "@esbuild/openbsd-x64": "0.25.9", - "@esbuild/openharmony-arm64": "0.25.9", - "@esbuild/sunos-x64": "0.25.9", - "@esbuild/win32-arm64": "0.25.9", - "@esbuild/win32-ia32": "0.25.9", - "@esbuild/win32-x64": "0.25.9" + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" } }, "node_modules/escalade": { @@ -10145,9 +10159,9 @@ } }, "node_modules/rollup": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.2.tgz", - "integrity": "sha512-BgLRGy7tNS9H66aIMASq1qSYbAAJV6Z6WR4QYTvj5FgF15rZ/ympT1uixHXwzbZUBDbkvqUI1KR0fH1FhMaQ9w==", + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.0.tgz", + "integrity": "sha512-+IuescNkTJQgX7AkIDtITipZdIGcWF0pnVvZTWStiazUmcGA2ag8dfg0urest2XlXUi9kuhfQ+qmdc5Stc3z7g==", "dev": true, "license": "MIT", "dependencies": { @@ -10161,27 +10175,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.50.2", - "@rollup/rollup-android-arm64": "4.50.2", - "@rollup/rollup-darwin-arm64": "4.50.2", - "@rollup/rollup-darwin-x64": "4.50.2", - "@rollup/rollup-freebsd-arm64": "4.50.2", - "@rollup/rollup-freebsd-x64": "4.50.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.50.2", - "@rollup/rollup-linux-arm-musleabihf": "4.50.2", - "@rollup/rollup-linux-arm64-gnu": "4.50.2", - "@rollup/rollup-linux-arm64-musl": "4.50.2", - "@rollup/rollup-linux-loong64-gnu": "4.50.2", - "@rollup/rollup-linux-ppc64-gnu": "4.50.2", - "@rollup/rollup-linux-riscv64-gnu": "4.50.2", - "@rollup/rollup-linux-riscv64-musl": "4.50.2", - "@rollup/rollup-linux-s390x-gnu": "4.50.2", - "@rollup/rollup-linux-x64-gnu": "4.50.2", - "@rollup/rollup-linux-x64-musl": "4.50.2", - "@rollup/rollup-openharmony-arm64": "4.50.2", - "@rollup/rollup-win32-arm64-msvc": "4.50.2", - "@rollup/rollup-win32-ia32-msvc": "4.50.2", - "@rollup/rollup-win32-x64-msvc": "4.50.2", + "@rollup/rollup-android-arm-eabi": "4.52.0", + "@rollup/rollup-android-arm64": "4.52.0", + "@rollup/rollup-darwin-arm64": "4.52.0", + "@rollup/rollup-darwin-x64": "4.52.0", + "@rollup/rollup-freebsd-arm64": "4.52.0", + "@rollup/rollup-freebsd-x64": "4.52.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.0", + "@rollup/rollup-linux-arm-musleabihf": "4.52.0", + "@rollup/rollup-linux-arm64-gnu": "4.52.0", + "@rollup/rollup-linux-arm64-musl": "4.52.0", + "@rollup/rollup-linux-loong64-gnu": "4.52.0", + "@rollup/rollup-linux-ppc64-gnu": "4.52.0", + "@rollup/rollup-linux-riscv64-gnu": "4.52.0", + "@rollup/rollup-linux-riscv64-musl": "4.52.0", + "@rollup/rollup-linux-s390x-gnu": "4.52.0", + "@rollup/rollup-linux-x64-gnu": "4.52.0", + "@rollup/rollup-linux-x64-musl": "4.52.0", + "@rollup/rollup-openharmony-arm64": "4.52.0", + "@rollup/rollup-win32-arm64-msvc": "4.52.0", + "@rollup/rollup-win32-ia32-msvc": "4.52.0", + "@rollup/rollup-win32-x64-gnu": "4.52.0", + "@rollup/rollup-win32-x64-msvc": "4.52.0", "fsevents": "~2.3.2" } }, @@ -10237,9 +10252,9 @@ "license": "MIT" }, "node_modules/sass": { - "version": "1.92.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.92.1.tgz", - "integrity": "sha512-ffmsdbwqb3XeyR8jJR6KelIXARM9bFQe8A6Q3W4Klmwy5Ckd5gz7jgUNHo4UOqutU5Sk1DtKLbpDP0nLCg1xqQ==", + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.93.0.tgz", + "integrity": "sha512-CQi5/AzCwiubU3dSqRDJ93RfOfg/hhpW1l6wCIvolmehfwgCI35R/0QDs1+R+Ygrl8jFawwwIojE2w47/mf94A==", "dev": true, "license": "MIT", "dependencies": { @@ -11554,9 +11569,9 @@ } }, "node_modules/vite": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz", - "integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.6.tgz", + "integrity": "sha512-SRYIB8t/isTwNn8vMB3MR6E+EQZM/WG1aKmmIUCfDXfVvKfc20ZpamngWHKzAmmu9ppsgxsg4b2I7c90JZudIQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/resources/assets/v1/src/locales/cs.json b/resources/assets/v1/src/locales/cs.json index 66c89e114d..f1040e3ae7 100644 --- a/resources/assets/v1/src/locales/cs.json +++ b/resources/assets/v1/src/locales/cs.json @@ -53,7 +53,7 @@ "external_url": "Extern\u00ed URL adresa", "update_transaction": "Aktualizovat transakci", "after_update_create_another": "Po aktualizaci se vr\u00e1tit sem pro pokra\u010dov\u00e1n\u00ed v \u00faprav\u00e1ch.", - "store_as_new": "Store as a new transaction instead of updating.", + "store_as_new": "Vytvo\u0159it novou transakci m\u00edsto aktualizov\u00e1n\u00ed t\u00e9 sou\u010dasn\u00e9.", "split_title_help": "Pokud vytvo\u0159\u00edte roz\u00fa\u010dtov\u00e1n\u00ed, je t\u0159eba, aby zde byl celkov\u00fd popis pro v\u0161echna roz\u00fa\u010dtov\u00e1n\u00ed dan\u00e9 transakce.", "none_in_select_list": "(\u017e\u00e1dn\u00e9)", "no_piggy_bank": "(\u017e\u00e1dn\u00e1 pokladni\u010dka)", diff --git a/resources/assets/v1/src/locales/it.json b/resources/assets/v1/src/locales/it.json index 08b853b545..e297e8cb29 100644 --- a/resources/assets/v1/src/locales/it.json +++ b/resources/assets/v1/src/locales/it.json @@ -2,9 +2,9 @@ "firefly": { "administrations_page_title": "Amministrazioni finanziarie", "administrations_index_menu": "Amministrazioni finanziarie", - "expires_at": "Expires at", - "temp_administrations_introduction": "Firefly III will soon get the ability to manage multiple financial administrations. Right now, you only have the one. You can set the title of this administration and its primary currency. This replaces the previous setting where you would set your \"default currency\". This setting is now tied to the financial administration and can be different per administration.", - "administration_currency_form_help": "It may take a long time for the page to load if you change the primary currency because transaction may need to be converted to your (new) primary currency.", + "expires_at": "Scade il", + "temp_administrations_introduction": "Firefly III avr\u00e0 presto la possibilit\u00e0 di gestire pi\u00f9 amministrazioni finanziarie. Al momento, ne hai solo una. Puoi impostare il titolo di questa amministrazione e la sua valuta principale. Questa impostazione sostituisce la precedente, che prevedeva di impostare la \"valuta predefinita\". Questa impostazione \u00e8 ora legata all'amministrazione finanziaria e pu\u00f2 essere diversa per ogni amministrazione.", + "administration_currency_form_help": "Se modifichi la valuta principale, il caricamento della pagina potrebbe richiedere molto tempo, poich\u00e9 potrebbe essere necessario convertire la transazione nella (nuova) valuta principale.", "administrations_page_edit_sub_title_js": "Modifica amministrazione finanziaria \"{title}\"", "table": "Tabella", "welcome_back": "La tua situazione finanziaria", @@ -102,23 +102,23 @@ "profile_oauth_client_secret_title": "Segreto del client", "profile_oauth_client_secret_expl": "Ecco il segreto del nuovo client. Questa \u00e8 l'unica occasione in cui viene mostrato pertanto non perderlo! Ora puoi usare questo segreto per effettuare delle richieste alle API.", "profile_oauth_confidential": "Riservato", - "profile_oauth_confidential_help": "Require the client to authenticate with a secret. Confidential clients can hold credentials in a secure way without exposing them to unauthorized parties. Public applications, such as native desktop or JavaScript SPA applications, are unable to hold secrets securely.", + "profile_oauth_confidential_help": "Richiedere al client di autenticarsi con un segreto. I client riservati possono conservare le credenziali in modo sicuro senza esporle a soggetti non autorizzati. Le applicazioni pubbliche, come le applicazioni desktop native o le applicazioni SPA JavaScript, non sono in grado di conservare i segreti in modo sicuro.", "multi_account_warning_unknown": "A seconda del tipo di transazione che hai creato, il conto di origine e\/o destinazione delle successive suddivisioni pu\u00f2 essere sovrascritto da qualsiasi cosa sia definita nella prima suddivisione della transazione.", "multi_account_warning_withdrawal": "Ricorda che il conto di origine delle successive suddivisioni verr\u00e0 sovrascritto da quello definito nella prima suddivisione del prelievo.", "multi_account_warning_deposit": "Ricorda che il conto di destinazione delle successive suddivisioni verr\u00e0 sovrascritto da quello definito nella prima suddivisione del deposito.", "multi_account_warning_transfer": "Ricorda che il conto di origine e il conto di destinazione delle successive suddivisioni verranno sovrascritti da quelli definiti nella prima suddivisione del trasferimento.", - "webhook_trigger_ANY": "After any event", + "webhook_trigger_ANY": "Dopo ogni evento", "webhook_trigger_STORE_TRANSACTION": "Dopo aver creato la transazione", "webhook_trigger_UPDATE_TRANSACTION": "Dopo aver aggiornato la transazione", "webhook_trigger_DESTROY_TRANSACTION": "Dopo aver eliminato la transazione", - "webhook_trigger_STORE_BUDGET": "After budget creation", - "webhook_trigger_UPDATE_BUDGET": "After budget update", - "webhook_trigger_DESTROY_BUDGET": "After budget delete", - "webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change", + "webhook_trigger_STORE_BUDGET": "Dopo la creazione del budget", + "webhook_trigger_UPDATE_BUDGET": "Dopo l'aggiornamento del budget", + "webhook_trigger_DESTROY_BUDGET": "Dopo l'eliminazione del budget", + "webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "Dopo la modifica dell'importo preventivato", "webhook_response_TRANSACTIONS": "Dettagli transazione", - "webhook_response_RELEVANT": "Relevant details", + "webhook_response_RELEVANT": "Dettagli rilevanti", "webhook_response_ACCOUNTS": "Dettagli conto", - "webhook_response_NONE": "No details", + "webhook_response_NONE": "Nessun dettaglio", "webhook_delivery_JSON": "JSON", "actions": "Azioni", "meta_data": "Meta dati", From 90623101a39999dc740d2335557df13f219ffe1a Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 21 Sep 2025 08:54:26 +0200 Subject: [PATCH 24/91] Add earned + spent, needs cleaning up still. --- .../v2/src/pages/dashboard/categories.js | 71 ++++++++++++------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/resources/assets/v2/src/pages/dashboard/categories.js b/resources/assets/v2/src/pages/dashboard/categories.js index 9af9c42d75..bc2707e805 100644 --- a/resources/assets/v2/src/pages/dashboard/categories.js +++ b/resources/assets/v2/src/pages/dashboard/categories.js @@ -54,12 +54,21 @@ export default () => ({ if (data.hasOwnProperty(i)) { let current = data[i]; let code = current.currency_code; - if (!series.hasOwnProperty(code)) { - series[code] = { - name: code, - yAxisID: '', - data: {}, - }; + + // create two series, "spent" and "earned". + for(const type of ['spent', 'earned']) { + let typeCode = code + '_' + type; + if (!series.hasOwnProperty(typeCode)) { + series[typeCode] = { + name: typeCode, + code: code, + type: type, + yAxisID: '', + data: {}, + }; + } + } + if (!currencies.includes(code)) { currencies.push(code); } } @@ -76,31 +85,38 @@ export default () => ({ code = current.primary_currency_code; } - // loop series, add 0 if not present or add actual amount. - for (const ii in series) { - if (series.hasOwnProperty(ii)) { - let amount = 0.0; - if (code === ii) { - // this series' currency matches this column's currency. - amount = parseFloat(current.entries.spent); - if(this.convertToPrimary) { - amount = parseFloat(current.entries.pc_entries.spent); + // twice again, for speny AND earned. + for(const type of ['spent', 'earned']) { + let typeCode = code + '_' + type; + // loop series, add 0 if not present or add actual amount. + for (const ii in series) { + if (series.hasOwnProperty(typeCode)) { + let amount = 0.0; + if (typeCode === ii) { + // this series' currency matches this column's currency. + amount = parseFloat(current.entries[type]); + if(this.convertToPrimary) { + amount = parseFloat(current.entries.pc_entries[type]); + } + yAxis = 'y' + typeCode; + } + if (series[typeCode].data.hasOwnProperty(current.label)) { + // there is a value for this particular currency. The amount from this column will be added. + // (even if this column isn't recorded in this currency and a new filler value is written) + // this is so currency conversion works. + series[typeCode].data[current.label] = series[typeCode].data[current.label] + amount; } - yAxis = 'y' + code; - } - if (series[ii].data.hasOwnProperty(current.label)) { - // there is a value for this particular currency. The amount from this column will be added. - // (even if this column isn't recorded in this currency and a new filler value is written) - // this is so currency conversion works. - series[ii].data[current.label] = series[ii].data[current.label] + amount; - } - if (!series[ii].data.hasOwnProperty(current.label)) { - // this column's amount is not yet set in this series. - series[ii].data[current.label] = amount; + if (!series[typeCode].data.hasOwnProperty(current.label)) { + // this column's amount is not yet set in this series. + series[typeCode].data[current.label] = amount; + } } } } + + + // add label to x-axis, not unimportant. if (!options.data.labels.includes(current.label)) { options.data.labels.push(current.label); @@ -111,9 +127,10 @@ export default () => ({ let count = 0; for (const i in series) { let yAxisID = 'y' + i; + let currencyCode = i.replace('_spent', '').replace('_earned', ''); let dataset = { label: i, - currency_code: i, + currency_code: currencyCode, yAxisID: yAxisID, data: [], // backgroundColor: getColors(null, 'background'), From 7d3b993b9851a9313e33051f828c83d367b5ecd5 Mon Sep 17 00:00:00 2001 From: JC5 Date: Sun, 21 Sep 2025 08:58:33 +0200 Subject: [PATCH 25/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-09-21?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/firefly.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/firefly.php b/config/firefly.php index 6bbe1f0ea2..c1c9db8f92 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -79,7 +79,7 @@ return [ // see cer.php for exchange rates feature flag. ], 'version' => 'develop/2025-09-21', - 'build_time' => 1758435980, + 'build_time' => 1758437799, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 26, From 7e08a1f33c78ceca161f2a84dcdaba8b743a2a3b Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 21 Sep 2025 15:11:16 +0200 Subject: [PATCH 26/91] Possible fix for #10940, not sure. --- app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php b/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php index 9dd940801a..bacf8d12c3 100644 --- a/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php +++ b/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php @@ -130,7 +130,7 @@ class PiggyBankEnrichment implements EnrichmentInterface } $this->amounts[$id][$accountId]['current_amount'] = bcadd($this->amounts[$id][$accountId]['current_amount'], (string) $item->current_amount); if (null !== $this->amounts[$id][$accountId]['pc_current_amount'] && null !== $item->native_current_amount) { - $this->amounts[$id][$accountId]['pc_current_amount'] = bcadd($this->amounts[$id][$accountId]['pc_current_amount'], $item->native_current_amount); + $this->amounts[$id][$accountId]['pc_current_amount'] = bcadd($this->amounts[$id][$accountId]['pc_current_amount'], (string) $item->native_current_amount); } } From 013c43f9f214aa7e4d7a3220fd4610154b3b0239 Mon Sep 17 00:00:00 2001 From: JC5 Date: Sun, 21 Sep 2025 15:15:08 +0200 Subject: [PATCH 27/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-09-21?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.lock | 16 ++++++++-------- config/firefly.php | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/composer.lock b/composer.lock index dce4114021..dc09cc5a05 100644 --- a/composer.lock +++ b/composer.lock @@ -11891,16 +11891,16 @@ }, { "name": "phpunit/phpunit", - "version": "12.3.11", + "version": "12.3.12", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "6a62f2b394e042884e4997ddc8b8db1ce56a0009" + "reference": "729861f66944204f5b446ee1cb156f02f2a439a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6a62f2b394e042884e4997ddc8b8db1ce56a0009", - "reference": "6a62f2b394e042884e4997ddc8b8db1ce56a0009", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/729861f66944204f5b446ee1cb156f02f2a439a6", + "reference": "729861f66944204f5b446ee1cb156f02f2a439a6", "shasum": "" }, "require": { @@ -11914,12 +11914,12 @@ "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.3", - "phpunit/php-code-coverage": "^12.3.7", + "phpunit/php-code-coverage": "^12.3.8", "phpunit/php-file-iterator": "^6.0.0", "phpunit/php-invoker": "^6.0.0", "phpunit/php-text-template": "^5.0.0", "phpunit/php-timer": "^8.0.0", - "sebastian/cli-parser": "^4.1.0", + "sebastian/cli-parser": "^4.2.0", "sebastian/comparator": "^7.1.3", "sebastian/diff": "^7.0.0", "sebastian/environment": "^8.0.3", @@ -11968,7 +11968,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.11" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.12" }, "funding": [ { @@ -11992,7 +11992,7 @@ "type": "tidelift" } ], - "time": "2025-09-14T06:21:44+00:00" + "time": "2025-09-21T12:23:01+00:00" }, { "name": "rector/rector", diff --git a/config/firefly.php b/config/firefly.php index c1c9db8f92..6aac08185a 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -79,7 +79,7 @@ return [ // see cer.php for exchange rates feature flag. ], 'version' => 'develop/2025-09-21', - 'build_time' => 1758437799, + 'build_time' => 1758460398, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 26, From a751218d53ddd65ad423e6ad5d5f0eb0f21479bd Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 21 Sep 2025 17:52:07 +0200 Subject: [PATCH 28/91] Fix rule order for #10938 --- app/TransactionRules/Engine/SearchRuleEngine.php | 5 +++-- config/firefly.php | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/TransactionRules/Engine/SearchRuleEngine.php b/app/TransactionRules/Engine/SearchRuleEngine.php index c4b4822ba4..a2164cf2fe 100644 --- a/app/TransactionRules/Engine/SearchRuleEngine.php +++ b/app/TransactionRules/Engine/SearchRuleEngine.php @@ -508,9 +508,10 @@ class SearchRuleEngine implements RuleEngineInterface { Log::debug(sprintf('Going to fire group #%d with %d rule(s)', $group->id, $group->rules->count())); + $rules = $group->rules()->orderBy('order', 'ASC')->get(); /** @var Rule $rule */ - foreach ($group->rules as $rule) { - Log::debug(sprintf('Going to fire rule #%d from group #%d', $rule->id, $group->id)); + foreach ($rules as $rule) { + Log::debug(sprintf('Going to fire rule #%d with order #%d from group #%d', $rule->id, $rule->order, $group->id)); $result = $this->fireRule($rule); if (true === $result && true === $rule->stop_processing) { Log::debug(sprintf('The rule was triggered and rule->stop_processing = true, so group #%d will stop processing further rules.', $group->id)); diff --git a/config/firefly.php b/config/firefly.php index c1c9db8f92..8f26b11816 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -81,7 +81,7 @@ return [ 'version' => 'develop/2025-09-21', 'build_time' => 1758437799, 'api_version' => '2.1.0', // field is no longer used. - 'db_version' => 26, + 'db_version' => 27, // generic settings 'maxUploadSize' => 1073741824, // 1 GB From e6b6a3cee5306cc3083d3414160c011d6c1632ad Mon Sep 17 00:00:00 2001 From: JC5 Date: Sun, 21 Sep 2025 17:57:23 +0200 Subject: [PATCH 29/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-09-21?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/TransactionRules/Engine/SearchRuleEngine.php | 1 + config/firefly.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/TransactionRules/Engine/SearchRuleEngine.php b/app/TransactionRules/Engine/SearchRuleEngine.php index a2164cf2fe..7843d1ffca 100644 --- a/app/TransactionRules/Engine/SearchRuleEngine.php +++ b/app/TransactionRules/Engine/SearchRuleEngine.php @@ -509,6 +509,7 @@ class SearchRuleEngine implements RuleEngineInterface Log::debug(sprintf('Going to fire group #%d with %d rule(s)', $group->id, $group->rules->count())); $rules = $group->rules()->orderBy('order', 'ASC')->get(); + /** @var Rule $rule */ foreach ($rules as $rule) { Log::debug(sprintf('Going to fire rule #%d with order #%d from group #%d', $rule->id, $rule->order, $group->id)); diff --git a/config/firefly.php b/config/firefly.php index cf8f2be9a7..d84b93a551 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -79,7 +79,7 @@ return [ // see cer.php for exchange rates feature flag. ], 'version' => 'develop/2025-09-21', - 'build_time' => 1758460398, + 'build_time' => 1758470121, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 27, From beecf9c22983246bf3bec632a63e4244812ff91d Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 21 Sep 2025 18:00:23 +0200 Subject: [PATCH 30/91] Make sure demo user cannot send notifications. --- app/Http/Controllers/Admin/NotificationController.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/Http/Controllers/Admin/NotificationController.php b/app/Http/Controllers/Admin/NotificationController.php index b8e52ebbe6..b2d6a40b62 100644 --- a/app/Http/Controllers/Admin/NotificationController.php +++ b/app/Http/Controllers/Admin/NotificationController.php @@ -29,6 +29,7 @@ use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\NotificationRequest; use FireflyIII\Notifications\Notifiables\OwnerNotifiable; use FireflyIII\Support\Facades\FireflyConfig; +use FireflyIII\User; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; @@ -122,6 +123,10 @@ class NotificationController extends Controller public function testNotification(Request $request): RedirectResponse { + if (true === auth()->user()->hasRole('demo')) { + session()->flash('error', (string) trans('firefly.not_available_demo_user' )); + return redirect(route('settings.notification.index')); + } $all = $request->all(); $channel = $all['test_submit'] ?? ''; From d868dc0945629cf1c473a885fa89666222cece22 Mon Sep 17 00:00:00 2001 From: JC5 Date: Mon, 22 Sep 2025 05:22:58 +0200 Subject: [PATCH 31/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-09-22?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/Admin/NotificationController.php | 4 ++-- config/firefly.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/Admin/NotificationController.php b/app/Http/Controllers/Admin/NotificationController.php index b2d6a40b62..233dc7f66a 100644 --- a/app/Http/Controllers/Admin/NotificationController.php +++ b/app/Http/Controllers/Admin/NotificationController.php @@ -29,7 +29,6 @@ use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\NotificationRequest; use FireflyIII\Notifications\Notifiables\OwnerNotifiable; use FireflyIII\Support\Facades\FireflyConfig; -use FireflyIII\User; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; @@ -124,7 +123,8 @@ class NotificationController extends Controller public function testNotification(Request $request): RedirectResponse { if (true === auth()->user()->hasRole('demo')) { - session()->flash('error', (string) trans('firefly.not_available_demo_user' )); + session()->flash('error', (string) trans('firefly.not_available_demo_user')); + return redirect(route('settings.notification.index')); } diff --git a/config/firefly.php b/config/firefly.php index d84b93a551..8248d97eb6 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -78,8 +78,8 @@ return [ 'running_balance_column' => env('USE_RUNNING_BALANCE', false), // see cer.php for exchange rates feature flag. ], - 'version' => 'develop/2025-09-21', - 'build_time' => 1758470121, + 'version' => 'develop/2025-09-22', + 'build_time' => 1758511276, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 27, From 5a1413e758b5e6987f2c52c0488d5e4e9119331b Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 23 Sep 2025 20:22:58 +0200 Subject: [PATCH 32/91] Fix #10954 --- resources/views/list/groups.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/list/groups.twig b/resources/views/list/groups.twig index 460462e684..a53f271fd8 100644 --- a/resources/views/list/groups.twig +++ b/resources/views/list/groups.twig @@ -268,7 +268,7 @@ {% if config('firefly.feature_flags.running_balance_column') %} - {% if null == transaction.balance_dirty or false == transaction.balance_dirty and null != transaction.destination_balance_after %} + {% if (null == transaction.balance_dirty or false == transaction.balance_dirty) and null != transaction.destination_balance_after and null != transaction.source_balance_after %} {% if transaction.transaction_type_type == 'Deposit' %} {{ formatAmountBySymbol(transaction.destination_balance_after, transaction.currency_symbol, transaction.currency_decimal_places) }} {% elseif transaction.transaction_type_type == 'Withdrawal' %} From 4a264f34fa30e9cfe031f13954d9576ec1bbd0d6 Mon Sep 17 00:00:00 2001 From: JC5 Date: Tue, 23 Sep 2025 20:41:36 +0200 Subject: [PATCH 33/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-09-23?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.lock | 102 ++++++++------ config/firefly.php | 4 +- package-lock.json | 330 +++++++++++++++++++++------------------------ 3 files changed, 210 insertions(+), 226 deletions(-) diff --git a/composer.lock b/composer.lock index dc09cc5a05..de8828fde5 100644 --- a/composer.lock +++ b/composer.lock @@ -1878,16 +1878,16 @@ }, { "name": "laravel/framework", - "version": "v12.30.1", + "version": "v12.31.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "7f61e8679f9142f282a0184ac7ef9e3834bfd023" + "reference": "281b711710c245dd8275d73132e92635be3094df" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/7f61e8679f9142f282a0184ac7ef9e3834bfd023", - "reference": "7f61e8679f9142f282a0184ac7ef9e3834bfd023", + "url": "https://api.github.com/repos/laravel/framework/zipball/281b711710c245dd8275d73132e92635be3094df", + "reference": "281b711710c245dd8275d73132e92635be3094df", "shasum": "" }, "require": { @@ -2094,7 +2094,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-09-18T21:07:07+00:00" + "time": "2025-09-23T15:33:04+00:00" }, { "name": "laravel/passport", @@ -2174,16 +2174,16 @@ }, { "name": "laravel/prompts", - "version": "v0.3.6", + "version": "v0.3.7", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "86a8b692e8661d0fb308cec64f3d176821323077" + "reference": "a1891d362714bc40c8d23b0b1d7090f022ea27cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/86a8b692e8661d0fb308cec64f3d176821323077", - "reference": "86a8b692e8661d0fb308cec64f3d176821323077", + "url": "https://api.github.com/repos/laravel/prompts/zipball/a1891d362714bc40c8d23b0b1d7090f022ea27cc", + "reference": "a1891d362714bc40c8d23b0b1d7090f022ea27cc", "shasum": "" }, "require": { @@ -2200,8 +2200,8 @@ "illuminate/collections": "^10.0|^11.0|^12.0", "mockery/mockery": "^1.5", "pestphp/pest": "^2.3|^3.4", - "phpstan/phpstan": "^1.11", - "phpstan/phpstan-mockery": "^1.1" + "phpstan/phpstan": "^1.12.28", + "phpstan/phpstan-mockery": "^1.1.3" }, "suggest": { "ext-pcntl": "Required for the spinner to be animated." @@ -2227,9 +2227,9 @@ "description": "Add beautiful and user-friendly forms to your command-line applications.", "support": { "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.3.6" + "source": "https://github.com/laravel/prompts/tree/v0.3.7" }, - "time": "2025-07-07T14:17:42+00:00" + "time": "2025-09-19T13:47:56+00:00" }, { "name": "laravel/sanctum", @@ -2297,16 +2297,16 @@ }, { "name": "laravel/serializable-closure", - "version": "v2.0.4", + "version": "v2.0.5", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "b352cf0534aa1ae6b4d825d1e762e35d43f8a841" + "reference": "3832547db6e0e2f8bb03d4093857b378c66eceed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/b352cf0534aa1ae6b4d825d1e762e35d43f8a841", - "reference": "b352cf0534aa1ae6b4d825d1e762e35d43f8a841", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/3832547db6e0e2f8bb03d4093857b378c66eceed", + "reference": "3832547db6e0e2f8bb03d4093857b378c66eceed", "shasum": "" }, "require": { @@ -2354,7 +2354,7 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2025-03-19T13:51:03+00:00" + "time": "2025-09-22T17:29:40+00:00" }, { "name": "laravel/slack-notification-channel", @@ -4235,24 +4235,26 @@ }, { "name": "paragonie/constant_time_encoding", - "version": "v3.0.0", + "version": "v3.1.1", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", - "reference": "df1e7fde177501eee2037dd159cf04f5f301a512" + "reference": "5e9b582660b997de205a84c02a3aac7c060900c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/df1e7fde177501eee2037dd159cf04f5f301a512", - "reference": "df1e7fde177501eee2037dd159cf04f5f301a512", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/5e9b582660b997de205a84c02a3aac7c060900c9", + "reference": "5e9b582660b997de205a84c02a3aac7c060900c9", "shasum": "" }, "require": { "php": "^8" }, "require-dev": { - "phpunit/phpunit": "^9", - "vimeo/psalm": "^4|^5" + "infection/infection": "^0", + "nikic/php-fuzzer": "^0", + "phpunit/phpunit": "^9|^10|^11", + "vimeo/psalm": "^4|^5|^6" }, "type": "library", "autoload": { @@ -4298,7 +4300,7 @@ "issues": "https://github.com/paragonie/constant_time_encoding/issues", "source": "https://github.com/paragonie/constant_time_encoding" }, - "time": "2024-05-08T12:36:18+00:00" + "time": "2025-09-22T21:00:33+00:00" }, { "name": "paragonie/random_compat", @@ -4352,16 +4354,16 @@ }, { "name": "phiki/phiki", - "version": "v2.0.3", + "version": "v2.0.4", "source": { "type": "git", "url": "https://github.com/phikiphp/phiki.git", - "reference": "fe51fe6dc31856cd776fd1b04ee74053a4271644" + "reference": "160785c50c01077780ab217e5808f00ab8f05a13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phikiphp/phiki/zipball/fe51fe6dc31856cd776fd1b04ee74053a4271644", - "reference": "fe51fe6dc31856cd776fd1b04ee74053a4271644", + "url": "https://api.github.com/repos/phikiphp/phiki/zipball/160785c50c01077780ab217e5808f00ab8f05a13", + "reference": "160785c50c01077780ab217e5808f00ab8f05a13", "shasum": "" }, "require": { @@ -4407,7 +4409,7 @@ "description": "Syntax highlighting using TextMate grammars in PHP.", "support": { "issues": "https://github.com/phikiphp/phiki/issues", - "source": "https://github.com/phikiphp/phiki/tree/v2.0.3" + "source": "https://github.com/phikiphp/phiki/tree/v2.0.4" }, "funding": [ { @@ -4419,7 +4421,7 @@ "type": "other" } ], - "time": "2025-09-19T11:50:41+00:00" + "time": "2025-09-20T17:21:02+00:00" }, { "name": "php-http/client-common", @@ -11891,16 +11893,16 @@ }, { "name": "phpunit/phpunit", - "version": "12.3.12", + "version": "12.3.13", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "729861f66944204f5b446ee1cb156f02f2a439a6" + "reference": "44f15312c4968fa8102e491fc6f3746410819c16" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/729861f66944204f5b446ee1cb156f02f2a439a6", - "reference": "729861f66944204f5b446ee1cb156f02f2a439a6", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/44f15312c4968fa8102e491fc6f3746410819c16", + "reference": "44f15312c4968fa8102e491fc6f3746410819c16", "shasum": "" }, "require": { @@ -11923,7 +11925,7 @@ "sebastian/comparator": "^7.1.3", "sebastian/diff": "^7.0.0", "sebastian/environment": "^8.0.3", - "sebastian/exporter": "^7.0.0", + "sebastian/exporter": "^7.0.1", "sebastian/global-state": "^8.0.2", "sebastian/object-enumerator": "^7.0.0", "sebastian/type": "^6.0.3", @@ -11968,7 +11970,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.12" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.13" }, "funding": [ { @@ -11992,7 +11994,7 @@ "type": "tidelift" } ], - "time": "2025-09-21T12:23:01+00:00" + "time": "2025-09-23T06:25:02+00:00" }, { "name": "rector/rector", @@ -12418,16 +12420,16 @@ }, { "name": "sebastian/exporter", - "version": "7.0.0", + "version": "7.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "76432aafc58d50691a00d86d0632f1217a47b688" + "reference": "b759164a8e02263784b662889cc6cbb686077af6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/76432aafc58d50691a00d86d0632f1217a47b688", - "reference": "76432aafc58d50691a00d86d0632f1217a47b688", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/b759164a8e02263784b662889cc6cbb686077af6", + "reference": "b759164a8e02263784b662889cc6cbb686077af6", "shasum": "" }, "require": { @@ -12484,15 +12486,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.0" + "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2025-02-07T04:56:42+00:00" + "time": "2025-09-22T05:39:29+00:00" }, { "name": "sebastian/global-state", diff --git a/config/firefly.php b/config/firefly.php index 8248d97eb6..4dbb9dfde7 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -78,8 +78,8 @@ return [ 'running_balance_column' => env('USE_RUNNING_BALANCE', false), // see cer.php for exchange rates feature flag. ], - 'version' => 'develop/2025-09-22', - 'build_time' => 1758511276, + 'version' => 'develop/2025-09-23', + 'build_time' => 1758652788, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 27, diff --git a/package-lock.json b/package-lock.json index a12d8faaf8..e0ac00f144 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2589,9 +2589,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.0.tgz", - "integrity": "sha512-VxDYCDqOaR7NXzAtvRx7G1u54d2kEHopb28YH/pKzY6y0qmogP3gG7CSiWsq9WvDFxOQMpNEyjVAHZFXfH3o/A==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.2.tgz", + "integrity": "sha512-o3pcKzJgSGt4d74lSZ+OCnHwkKBeAbFDmbEm5gg70eA8VkyCuC/zV9TwBnmw6VjDlRdF4Pshfb+WE9E6XY1PoQ==", "cpu": [ "arm" ], @@ -2603,9 +2603,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.0.tgz", - "integrity": "sha512-pqDirm8koABIKvzL59YI9W9DWbRlTX7RWhN+auR8HXJxo89m4mjqbah7nJZjeKNTNYopqL+yGg+0mhCpf3xZtQ==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.2.tgz", + "integrity": "sha512-cqFSWO5tX2vhC9hJTK8WAiPIm4Q8q/cU8j2HQA0L3E1uXvBYbOZMhE2oFL8n2pKB5sOCHY6bBuHaRwG7TkfJyw==", "cpu": [ "arm64" ], @@ -2617,9 +2617,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.0.tgz", - "integrity": "sha512-YCdWlY/8ltN6H78HnMsRHYlPiKvqKagBP1r+D7SSylxX+HnsgXGCmLiV3Y4nSyY9hW8qr8U9LDUx/Lo7M6MfmQ==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.2.tgz", + "integrity": "sha512-vngduywkkv8Fkh3wIZf5nFPXzWsNsVu1kvtLETWxTFf/5opZmflgVSeLgdHR56RQh71xhPhWoOkEBvbehwTlVA==", "cpu": [ "arm64" ], @@ -2631,9 +2631,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.0.tgz", - "integrity": "sha512-z4nw6y1j+OOSGzuVbSWdIp1IUks9qNw4dc7z7lWuWDKojY38VMWBlEN7F9jk5UXOkUcp97vA1N213DF+Lz8BRg==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.2.tgz", + "integrity": "sha512-h11KikYrUCYTrDj6h939hhMNlqU2fo/X4NB0OZcys3fya49o1hmFaczAiJWVAFgrM1NCP6RrO7lQKeVYSKBPSQ==", "cpu": [ "x64" ], @@ -2645,9 +2645,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.0.tgz", - "integrity": "sha512-Q/dv9Yvyr5rKlK8WQJZVrp5g2SOYeZUs9u/t2f9cQ2E0gJjYB/BWoedXfUT0EcDJefi2zzVfhcOj8drWCzTviw==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.2.tgz", + "integrity": "sha512-/eg4CI61ZUkLXxMHyVlmlGrSQZ34xqWlZNW43IAU4RmdzWEx0mQJ2mN/Cx4IHLVZFL6UBGAh+/GXhgvGb+nVxw==", "cpu": [ "arm64" ], @@ -2659,9 +2659,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.0.tgz", - "integrity": "sha512-kdBsLs4Uile/fbjZVvCRcKB4q64R+1mUq0Yd7oU1CMm1Av336ajIFqNFovByipciuUQjBCPMxwJhCgfG2re3rg==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.2.tgz", + "integrity": "sha512-QOWgFH5X9+p+S1NAfOqc0z8qEpJIoUHf7OWjNUGOeW18Mx22lAUOiA9b6r2/vpzLdfxi/f+VWsYjUOMCcYh0Ng==", "cpu": [ "x64" ], @@ -2673,9 +2673,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.0.tgz", - "integrity": "sha512-aL6hRwu0k7MTUESgkg7QHY6CoqPgr6gdQXRJI1/VbFlUMwsSzPGSR7sG5d+MCbYnJmJwThc2ol3nixj1fvI/zQ==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.2.tgz", + "integrity": "sha512-kDWSPafToDd8LcBYd1t5jw7bD5Ojcu12S3uT372e5HKPzQt532vW+rGFFOaiR0opxePyUkHrwz8iWYEyH1IIQA==", "cpu": [ "arm" ], @@ -2687,9 +2687,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.0.tgz", - "integrity": "sha512-BTs0M5s1EJejgIBJhCeiFo7GZZ2IXWkFGcyZhxX4+8usnIo5Mti57108vjXFIQmmJaRyDwmV59Tw64Ap1dkwMw==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.2.tgz", + "integrity": "sha512-gKm7Mk9wCv6/rkzwCiUC4KnevYhlf8ztBrDRT9g/u//1fZLapSRc+eDZj2Eu2wpJ+0RzUKgtNijnVIB4ZxyL+w==", "cpu": [ "arm" ], @@ -2701,9 +2701,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.0.tgz", - "integrity": "sha512-uj672IVOU9m08DBGvoPKPi/J8jlVgjh12C9GmjjBxCTQc3XtVmRkRKyeHSmIKQpvJ7fIm1EJieBUcnGSzDVFyw==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.2.tgz", + "integrity": "sha512-66lA8vnj5mB/rtDNwPgrrKUOtCLVQypkyDa2gMfOefXK6rcZAxKLO9Fy3GkW8VkPnENv9hBkNOFfGLf6rNKGUg==", "cpu": [ "arm64" ], @@ -2715,9 +2715,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.0.tgz", - "integrity": "sha512-/+IVbeDMDCtB/HP/wiWsSzduD10SEGzIZX2945KSgZRNi4TSkjHqRJtNTVtVb8IRwhJ65ssI56krlLik+zFWkw==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.2.tgz", + "integrity": "sha512-s+OPucLNdJHvuZHuIz2WwncJ+SfWHFEmlC5nKMUgAelUeBUnlB4wt7rXWiyG4Zn07uY2Dd+SGyVa9oyLkVGOjA==", "cpu": [ "arm64" ], @@ -2729,9 +2729,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.0.tgz", - "integrity": "sha512-U1vVzvSWtSMWKKrGoROPBXMh3Vwn93TA9V35PldokHGqiUbF6erSzox/5qrSMKp6SzakvyjcPiVF8yB1xKr9Pg==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.2.tgz", + "integrity": "sha512-8wTRM3+gVMDLLDdaT6tKmOE3lJyRy9NpJUS/ZRWmLCmOPIJhVyXwjBo+XbrrwtV33Em1/eCTd5TuGJm4+DmYjw==", "cpu": [ "loong64" ], @@ -2743,9 +2743,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.0.tgz", - "integrity": "sha512-X/4WfuBAdQRH8cK3DYl8zC00XEE6aM472W+QCycpQJeLWVnHfkv7RyBFVaTqNUMsTgIX8ihMjCvFF9OUgeABzw==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.2.tgz", + "integrity": "sha512-6yqEfgJ1anIeuP2P/zhtfBlDpXUb80t8DpbYwXQ3bQd95JMvUaqiX+fKqYqUwZXqdJDd8xdilNtsHM2N0cFm6A==", "cpu": [ "ppc64" ], @@ -2757,9 +2757,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.0.tgz", - "integrity": "sha512-xIRYc58HfWDBZoLmWfWXg2Sq8VCa2iJ32B7mqfWnkx5mekekl0tMe7FHpY8I72RXEcUkaWawRvl3qA55og+cwQ==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.2.tgz", + "integrity": "sha512-sshYUiYVSEI2B6dp4jMncwxbrUqRdNApF2c3bhtLAU0qA8Lrri0p0NauOsTWh3yCCCDyBOjESHMExonp7Nzc0w==", "cpu": [ "riscv64" ], @@ -2771,9 +2771,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.0.tgz", - "integrity": "sha512-mbsoUey05WJIOz8U1WzNdf+6UMYGwE3fZZnQqsM22FZ3wh1N887HT6jAOjXs6CNEK3Ntu2OBsyQDXfIjouI4dw==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.2.tgz", + "integrity": "sha512-duBLgd+3pqC4MMwBrKkFxaZerUxZcYApQVC5SdbF5/e/589GwVvlRUnyqMFbM8iUSb1BaoX/3fRL7hB9m2Pj8Q==", "cpu": [ "riscv64" ], @@ -2785,9 +2785,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.0.tgz", - "integrity": "sha512-qP6aP970bucEi5KKKR4AuPFd8aTx9EF6BvutvYxmZuWLJHmnq4LvBfp0U+yFDMGwJ+AIJEH5sIP+SNypauMWzg==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.2.tgz", + "integrity": "sha512-tzhYJJidDUVGMgVyE+PmxENPHlvvqm1KILjjZhB8/xHYqAGeizh3GBGf9u6WdJpZrz1aCpIIHG0LgJgH9rVjHQ==", "cpu": [ "s390x" ], @@ -2799,9 +2799,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.0.tgz", - "integrity": "sha512-nmSVN+F2i1yKZ7rJNKO3G7ZzmxJgoQBQZ/6c4MuS553Grmr7WqR7LLDcYG53Z2m9409z3JLt4sCOhLdbKQ3HmA==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.2.tgz", + "integrity": "sha512-opH8GSUuVcCSSyHHcl5hELrmnk4waZoVpgn/4FDao9iyE4WpQhyWJ5ryl5M3ocp4qkRuHfyXnGqg8M9oKCEKRA==", "cpu": [ "x64" ], @@ -2813,9 +2813,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.0.tgz", - "integrity": "sha512-2d0qRo33G6TfQVjaMR71P+yJVGODrt5V6+T0BDYH4EMfGgdC/2HWDVjSSFw888GSzAZUwuska3+zxNUCDco6rQ==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.2.tgz", + "integrity": "sha512-LSeBHnGli1pPKVJ79ZVJgeZWWZXkEe/5o8kcn23M8eMKCUANejchJbF/JqzM4RRjOJfNRhKJk8FuqL1GKjF5oQ==", "cpu": [ "x64" ], @@ -2827,9 +2827,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.0.tgz", - "integrity": "sha512-A1JalX4MOaFAAyGgpO7XP5khquv/7xKzLIyLmhNrbiCxWpMlnsTYr8dnsWM7sEeotNmxvSOEL7F65j0HXFcFsw==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.2.tgz", + "integrity": "sha512-uPj7MQ6/s+/GOpolavm6BPo+6CbhbKYyZHUDvZ/SmJM7pfDBgdGisFX3bY/CBDMg2ZO4utfhlApkSfZ92yXw7Q==", "cpu": [ "arm64" ], @@ -2841,9 +2841,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.0.tgz", - "integrity": "sha512-YQugafP/rH0eOOHGjmNgDURrpYHrIX0yuojOI8bwCyXwxC9ZdTd3vYkmddPX0oHONLXu9Rb1dDmT0VNpjkzGGw==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.2.tgz", + "integrity": "sha512-Z9MUCrSgIaUeeHAiNkm3cQyst2UhzjPraR3gYYfOjAuZI7tcFRTOD+4cHLPoS/3qinchth+V56vtqz1Tv+6KPA==", "cpu": [ "arm64" ], @@ -2855,9 +2855,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.0.tgz", - "integrity": "sha512-zYdUYhi3Qe2fndujBqL5FjAFzvNeLxtIqfzNEVKD1I7C37/chv1VxhscWSQHTNfjPCrBFQMnynwA3kpZpZ8w4A==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.2.tgz", + "integrity": "sha512-+GnYBmpjldD3XQd+HMejo+0gJGwYIOfFeoBQv32xF/RUIvccUz20/V6Otdv+57NE70D5pa8W/jVGDoGq0oON4A==", "cpu": [ "ia32" ], @@ -2869,9 +2869,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.0.tgz", - "integrity": "sha512-fGk03kQylNaCOQ96HDMeT7E2n91EqvCDd3RwvT5k+xNdFCeMGnj5b5hEgTGrQuyidqSsD3zJDQ21QIaxXqTBJw==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.2.tgz", + "integrity": "sha512-ApXFKluSB6kDQkAqZOKXBjiaqdF1BlKi+/eqnYe9Ee7U2K3pUDKsIyr8EYm/QDHTJIM+4X+lI0gJc3TTRhd+dA==", "cpu": [ "x64" ], @@ -2883,9 +2883,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.0.tgz", - "integrity": "sha512-6iKDCVSIUQ8jPMoIV0OytRKniaYyy5EbY/RRydmLW8ZR3cEBhxbWl5ro0rkUNe0ef6sScvhbY79HrjRm8i3vDQ==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.2.tgz", + "integrity": "sha512-ARz+Bs8kY6FtitYM96PqPEVvPXqEZmPZsSkXvyX19YzDqkCaIlhCieLLMI5hxO9SRZ2XtCtm8wxhy0iJ2jxNfw==", "cpu": [ "x64" ], @@ -4329,18 +4329,17 @@ } }, "node_modules/browserify-sign": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", - "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.4.tgz", + "integrity": "sha512-pbZw0FHibrwXcpLQlXwHp21A5undDBo+RaGNL0K3KOm8nK8uP6PThhS301VDzoMgURZPiVRWRrVHlo6NyU57kA==", "dev": true, "license": "ISC", "dependencies": { - "bn.js": "^5.2.1", - "browserify-rsa": "^4.1.0", + "bn.js": "^5.2.2", + "browserify-rsa": "^4.1.1", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", - "elliptic": "^6.5.5", - "hash-base": "~3.0", + "elliptic": "^6.6.1", "inherits": "^2.0.4", "parse-asn1": "^5.1.7", "readable-stream": "^2.3.8", @@ -5736,9 +5735,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.222", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.222.tgz", - "integrity": "sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w==", + "version": "1.5.223", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.223.tgz", + "integrity": "sha512-qKm55ic6nbEmagFlTFczML33rF90aU+WtrJ9MdTCThrcvDNdUHN4p6QfVN78U06ZmguqXIyMPyYhw2TrbDUwPQ==", "dev": true, "license": "ISC" }, @@ -8761,21 +8760,20 @@ } }, "node_modules/parse-asn1": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", - "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.8.tgz", + "integrity": "sha512-e90aVPe/1q/g7BrNeYvbJy++5tln4ShE+I3qZ5LxFpUbu+uavfKMuzH2R3SH141O7Pvruwif0BZRwKoVf6vW6w==", "dev": true, "license": "ISC", "dependencies": { "asn1.js": "^4.10.1", "browserify-aes": "^1.2.0", "evp_bytestokey": "^1.0.3", - "hash-base": "~3.0", - "pbkdf2": "^3.1.2", + "pbkdf2": "^3.1.3", "safe-buffer": "^5.2.1" }, "engines": { - "node": ">= 0.10" + "node": ">= 0.12" } }, "node_modules/parse-json": { @@ -8937,57 +8935,23 @@ } }, "node_modules/pbkdf2": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.3.tgz", - "integrity": "sha512-wfRLBZ0feWRhCIkoMB6ete7czJcnNnqRpcoWQBLqatqXXmelSRqfdDK4F3u9T2s2cXas/hQJcryI/4lAL+XTlA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.4.tgz", + "integrity": "sha512-0yPXNT01PxSUdkIxL85Fd+yPdeCcvGwFPAAHbR3Z2ukMERcRrJFfLUKK3oglbQ9eUPeX6qDY3QiELqiDarZYUQ==", "dev": true, "license": "MIT", "dependencies": { - "create-hash": "~1.1.3", + "create-hash": "^1.2.0", "create-hmac": "^1.1.7", - "ripemd160": "=2.0.1", + "ripemd160": "^2.0.3", "safe-buffer": "^5.2.1", - "sha.js": "^2.4.11", - "to-buffer": "^1.2.0" + "sha.js": "^2.4.12", + "to-buffer": "^1.2.1" }, "engines": { "node": ">=0.12" } }, - "node_modules/pbkdf2/node_modules/create-hash": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", - "integrity": "sha512-snRpch/kwQhcdlnZKYanNF1m0RDlrCdSKQaH87w1FCFPVPNCQ/Il9QJKAX2jVBZddRdaHBMC+zXa9Gw9tmkNUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "sha.js": "^2.4.0" - } - }, - "node_modules/pbkdf2/node_modules/hash-base": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", - "integrity": "sha512-0TROgQ1/SxE6KmxWSvXHvRj90/Xo1JvZShofnYF+f6ZsGtR4eES7WfrQzPalmyagfKZCXpVnitiRebZulWsbiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.1" - } - }, - "node_modules/pbkdf2/node_modules/ripemd160": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", - "integrity": "sha512-J7f4wutN8mdbV08MJnXibYpCOPHR+yzy+iQ/AsjMv2j8cLavQ8VGagDFUwwTAdF8FmRKVeNpbTTEwNHCW1g94w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hash-base": "^2.0.0", - "inherits": "^2.0.1" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -9961,16 +9925,16 @@ } }, "node_modules/regexpu-core": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.3.1.tgz", - "integrity": "sha512-DzcswPr252wEr7Qz8AyAVbfyBDKLoYp6eRA1We2Fa9qirRFSdtkP5sHr3yglDKy2BbA0fd2T+j/CUSKes3FeVQ==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", + "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", "dev": true, "license": "MIT", "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", - "regjsparser": "^0.12.0", + "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" }, @@ -9986,31 +9950,18 @@ "license": "MIT" }, "node_modules/regjsparser": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.0.tgz", + "integrity": "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "jsesc": "~3.0.2" + "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", @@ -10148,20 +10099,39 @@ } }, "node_modules/ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.3.tgz", + "integrity": "sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==", "dev": true, "license": "MIT", "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" + "hash-base": "^3.1.2", + "inherits": "^2.0.4" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ripemd160/node_modules/hash-base": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.2.tgz", + "integrity": "sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.1" + }, + "engines": { + "node": ">= 0.8" } }, "node_modules/rollup": { - "version": "4.52.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.0.tgz", - "integrity": "sha512-+IuescNkTJQgX7AkIDtITipZdIGcWF0pnVvZTWStiazUmcGA2ag8dfg0urest2XlXUi9kuhfQ+qmdc5Stc3z7g==", + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.2.tgz", + "integrity": "sha512-I25/2QgoROE1vYV+NQ1En9T9UFB9Cmfm2CJ83zZOlaDpvz29wGQSZXWKw7MiNXau7wYgB/T9fVIdIuEQ+KbiiA==", "dev": true, "license": "MIT", "dependencies": { @@ -10175,28 +10145,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.52.0", - "@rollup/rollup-android-arm64": "4.52.0", - "@rollup/rollup-darwin-arm64": "4.52.0", - "@rollup/rollup-darwin-x64": "4.52.0", - "@rollup/rollup-freebsd-arm64": "4.52.0", - "@rollup/rollup-freebsd-x64": "4.52.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.52.0", - "@rollup/rollup-linux-arm-musleabihf": "4.52.0", - "@rollup/rollup-linux-arm64-gnu": "4.52.0", - "@rollup/rollup-linux-arm64-musl": "4.52.0", - "@rollup/rollup-linux-loong64-gnu": "4.52.0", - "@rollup/rollup-linux-ppc64-gnu": "4.52.0", - "@rollup/rollup-linux-riscv64-gnu": "4.52.0", - "@rollup/rollup-linux-riscv64-musl": "4.52.0", - "@rollup/rollup-linux-s390x-gnu": "4.52.0", - "@rollup/rollup-linux-x64-gnu": "4.52.0", - "@rollup/rollup-linux-x64-musl": "4.52.0", - "@rollup/rollup-openharmony-arm64": "4.52.0", - "@rollup/rollup-win32-arm64-msvc": "4.52.0", - "@rollup/rollup-win32-ia32-msvc": "4.52.0", - "@rollup/rollup-win32-x64-gnu": "4.52.0", - "@rollup/rollup-win32-x64-msvc": "4.52.0", + "@rollup/rollup-android-arm-eabi": "4.52.2", + "@rollup/rollup-android-arm64": "4.52.2", + "@rollup/rollup-darwin-arm64": "4.52.2", + "@rollup/rollup-darwin-x64": "4.52.2", + "@rollup/rollup-freebsd-arm64": "4.52.2", + "@rollup/rollup-freebsd-x64": "4.52.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.2", + "@rollup/rollup-linux-arm-musleabihf": "4.52.2", + "@rollup/rollup-linux-arm64-gnu": "4.52.2", + "@rollup/rollup-linux-arm64-musl": "4.52.2", + "@rollup/rollup-linux-loong64-gnu": "4.52.2", + "@rollup/rollup-linux-ppc64-gnu": "4.52.2", + "@rollup/rollup-linux-riscv64-gnu": "4.52.2", + "@rollup/rollup-linux-riscv64-musl": "4.52.2", + "@rollup/rollup-linux-s390x-gnu": "4.52.2", + "@rollup/rollup-linux-x64-gnu": "4.52.2", + "@rollup/rollup-linux-x64-musl": "4.52.2", + "@rollup/rollup-openharmony-arm64": "4.52.2", + "@rollup/rollup-win32-arm64-msvc": "4.52.2", + "@rollup/rollup-win32-ia32-msvc": "4.52.2", + "@rollup/rollup-win32-x64-gnu": "4.52.2", + "@rollup/rollup-win32-x64-msvc": "4.52.2", "fsevents": "~2.3.2" } }, @@ -10252,9 +10222,9 @@ "license": "MIT" }, "node_modules/sass": { - "version": "1.93.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.93.0.tgz", - "integrity": "sha512-CQi5/AzCwiubU3dSqRDJ93RfOfg/hhpW1l6wCIvolmehfwgCI35R/0QDs1+R+Ygrl8jFawwwIojE2w47/mf94A==", + "version": "1.93.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.93.1.tgz", + "integrity": "sha512-wLAeLB7IksO2u+cCfhHqcy7/2ZUMPp/X2oV6+LjmweTqgjhOKrkaE/Q1wljxtco5EcOcupZ4c981X0gpk5Tiag==", "dev": true, "license": "MIT", "dependencies": { @@ -11569,9 +11539,9 @@ } }, "node_modules/vite": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.6.tgz", - "integrity": "sha512-SRYIB8t/isTwNn8vMB3MR6E+EQZM/WG1aKmmIUCfDXfVvKfc20ZpamngWHKzAmmu9ppsgxsg4b2I7c90JZudIQ==", + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.7.tgz", + "integrity": "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==", "dev": true, "license": "MIT", "dependencies": { From 855bc2f8e742f5f54e6a6ebb777e6980ef279824 Mon Sep 17 00:00:00 2001 From: Sander Dorigo Date: Wed, 24 Sep 2025 14:39:54 +0200 Subject: [PATCH 34/91] attempted fix for #10956 --- .../V1/Controllers/Webhook/ShowController.php | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/app/Api/V1/Controllers/Webhook/ShowController.php b/app/Api/V1/Controllers/Webhook/ShowController.php index c0a27d5fce..a24d5806f4 100644 --- a/app/Api/V1/Controllers/Webhook/ShowController.php +++ b/app/Api/V1/Controllers/Webhook/ShowController.php @@ -158,18 +158,23 @@ class ShowController extends Controller Log::debug(sprintf('Now in triggerTransaction(%d, %d)', $webhook->id, $group->id)); Log::channel('audit')->info(sprintf('User triggers webhook #%d on transaction group #%d.', $webhook->id, $group->id)); - /** @var MessageGeneratorInterface $engine */ - $engine = app(MessageGeneratorInterface::class); - $engine->setUser(auth()->user()); - // tell the generator which trigger it should look for - $engine->setTrigger(WebhookTrigger::tryFrom($webhook->trigger)); - // tell the generator which objects to process - $engine->setObjects(new Collection()->push($group)); - // set the webhook to trigger - $engine->setWebhooks(new Collection()->push($webhook)); - // tell the generator to generate the messages - $engine->generateMessages(); + /** @var \FireflyIII\Models\WebhookTrigger $trigger */ + foreach($webhook->webhookTriggers as $trigger) { + /** @var MessageGeneratorInterface $engine */ + $engine = app(MessageGeneratorInterface::class); + $engine->setUser(auth()->user()); + + // tell the generator which trigger it should look for + $engine->setTrigger(WebhookTrigger::tryFrom((int)$trigger->key)); + // tell the generator which objects to process + $engine->setObjects(new Collection()->push($group)); + // set the webhook to trigger + $engine->setWebhooks(new Collection()->push($webhook)); + // tell the generator to generate the messages + $engine->generateMessages(); + } + // trigger event to send them: Log::debug('send event RequestedSendWebhookMessages from ShowController::triggerTransaction()'); From 0aa90b945395180f7048a218b8f0b496457ab2d1 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 24 Sep 2025 19:51:39 +0200 Subject: [PATCH 35/91] Fix #10960 --- app/Exceptions/GracefulNotFoundHandler.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Exceptions/GracefulNotFoundHandler.php b/app/Exceptions/GracefulNotFoundHandler.php index fe0ca9c896..b2feef6ea6 100644 --- a/app/Exceptions/GracefulNotFoundHandler.php +++ b/app/Exceptions/GracefulNotFoundHandler.php @@ -86,6 +86,7 @@ class GracefulNotFoundHandler extends ExceptionHandler return $this->handleAttachment($request, $e); case 'bills.show': + case 'subscriptions.show': $request->session()->reflash(); return redirect(route('bills.index')); From 62c5440605347895cdcf2480026bead32d6a8139 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 25 Sep 2025 19:07:02 +0200 Subject: [PATCH 36/91] Add table for period statistics, and see what happens re: performance. --- app/Models/Account.php | 8 + app/Models/PeriodStatistic.php | 56 +++ app/Providers/FireflyServiceProvider.php | 3 + .../PeriodStatisticRepository.php | 65 +++ .../PeriodStatisticRepositoryInterface.php | 38 ++ .../Http/Controllers/PeriodOverview.php | 387 ++++++++++-------- ..._09_25_175248_create_period_statistics.php | 47 +++ 7 files changed, 433 insertions(+), 171 deletions(-) create mode 100644 app/Models/PeriodStatistic.php create mode 100644 app/Repositories/PeriodStatistic/PeriodStatisticRepository.php create mode 100644 app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php create mode 100644 database/migrations/2025_09_25_175248_create_period_statistics.php diff --git a/app/Models/Account.php b/app/Models/Account.php index e76ba418ee..82c6369aff 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -241,4 +241,12 @@ class Account extends Model get: static fn ($value) => (string)$value, ); } + + public function primaryPeriodStatistics(): MorphMany + + { + + return $this->morphMany(PeriodStatistic::class, 'primary_statable'); + + } } diff --git a/app/Models/PeriodStatistic.php b/app/Models/PeriodStatistic.php new file mode 100644 index 0000000000..0eb7d1f9b7 --- /dev/null +++ b/app/Models/PeriodStatistic.php @@ -0,0 +1,56 @@ + 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'date' => SeparateTimezoneCaster::class, + ]; + } + + protected function count(): Attribute + { + return Attribute::make( + get: static fn ($value) => (int)$value, + ); + } + + + public function primaryStatable(): MorphTo + { + + return $this->morphTo(); + + } + + public function secondaryStatable(): MorphTo + { + + return $this->morphTo(); + + } + public function tertiaryStatable(): MorphTo + { + + return $this->morphTo(); + + } + + // +} diff --git a/app/Providers/FireflyServiceProvider.php b/app/Providers/FireflyServiceProvider.php index 02ff3d729b..cff9567090 100644 --- a/app/Providers/FireflyServiceProvider.php +++ b/app/Providers/FireflyServiceProvider.php @@ -43,6 +43,8 @@ use FireflyIII\Repositories\AuditLogEntry\ALERepository; use FireflyIII\Repositories\AuditLogEntry\ALERepositoryInterface; use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepository; use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepositoryInterface; +use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepository; +use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface; use FireflyIII\Repositories\TransactionType\TransactionTypeRepository; use FireflyIII\Repositories\TransactionType\TransactionTypeRepositoryInterface; use FireflyIII\Repositories\User\UserRepository; @@ -161,6 +163,7 @@ class FireflyServiceProvider extends ServiceProvider $this->app->bind(AttachmentHelperInterface::class, AttachmentHelper::class); $this->app->bind(ALERepositoryInterface::class, ALERepository::class); + $this->app->bind(PeriodStatisticRepositoryInterface::class, PeriodStatisticRepository::class); $this->app->bind( static function (Application $app): ObjectGroupRepositoryInterface { diff --git a/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php new file mode 100644 index 0000000000..f7665f2b6d --- /dev/null +++ b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php @@ -0,0 +1,65 @@ +. + */ + +namespace FireflyIII\Repositories\PeriodStatistic; + +use Carbon\Carbon; +use FireflyIII\Models\PeriodStatistic; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Collection; + +class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface +{ + + public function findPeriodStatistics(Model $model, Carbon $start, Carbon $end, array $types): Collection + { + return $model->primaryPeriodStatistics() + ->where('start', $start) + ->where('end', $end) + ->whereIn('type', $types) + ->get(); + } + + public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection + { + return $model->primaryPeriodStatistics() + ->where('start', $start) + ->where('end', $end) + ->where('type', $type) + ->get(); + } + + public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic + { + $stat = new PeriodStatistic; + $stat->primaryStatable()->associate($model); + $stat->transaction_currency_id = $currencyId; + $stat->start = $start; + $stat->start_tz = $start->format('e'); + $stat->end = $end; + $stat->end_tz = $end->format('e'); + $stat->amount = $amount; + $stat->count = $count; + $stat->type = $type; + $stat->save(); + return $stat; + } +} diff --git a/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php b/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php new file mode 100644 index 0000000000..3d6245022a --- /dev/null +++ b/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php @@ -0,0 +1,38 @@ +. + */ + +namespace FireflyIII\Repositories\PeriodStatistic; + +use Carbon\Carbon; +use FireflyIII\Models\PeriodStatistic; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Collection; + +interface PeriodStatisticRepositoryInterface +{ + + public function findPeriodStatistics(Model $model, Carbon $start, Carbon $end, array $types): Collection; + + public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection; + + public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic; + +} diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index afbc198ce2..6944eb5f26 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -30,11 +30,13 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Account; use FireflyIII\Models\Category; +use FireflyIII\Models\PeriodStatistic; use FireflyIII\Models\Tag; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; +use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface; use FireflyIII\Support\CacheProperties; -use FireflyIII\Support\Debug\Timer; +use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\Navigation; use Illuminate\Support\Facades\Log; @@ -67,8 +69,9 @@ use Illuminate\Support\Facades\Log; */ trait PeriodOverview { - protected AccountRepositoryInterface $accountRepository; - protected JournalRepositoryInterface $journalRepos; + protected AccountRepositoryInterface $accountRepository; + protected JournalRepositoryInterface $journalRepos; + protected PeriodStatisticRepositoryInterface $periodStatisticRepo; /** * This method returns "period entries", so nov-2015, dec-2015, etc. (this depends on the users session range) @@ -79,71 +82,107 @@ trait PeriodOverview */ protected function getAccountPeriodOverview(Account $account, Carbon $start, Carbon $end): array { - Log::debug('Now in getAccountPeriodOverview()'); - $timer = Timer::getInstance(); - $timer->start('account-period-total'); - $this->accountRepository = app(AccountRepositoryInterface::class); - $range = Navigation::getViewRange(true); - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; - - // properties for cache - $cache = new CacheProperties(); - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty('account-show-period-entries'); - $cache->addProperty($account->id); - if ($cache->has()) { - Log::debug('Return CACHED in getAccountPeriodOverview()'); - - return $cache->get(); - } - + Log::debug(sprintf('Now in getAccountPeriodOverview(#%d, %s %s)', $account->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); + $this->accountRepository = app(AccountRepositoryInterface::class); + $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); + $range = Navigation::getViewRange(true); + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - - // run a custom query because doing this with the collector is MEGA slow. - $timer->start('account-period-collect'); - $transactions = $this->accountRepository->periodCollection($account, $start, $end); - $timer->stop('account-period-collect'); - // loop dates + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; Log::debug(sprintf('Count of loops: %d', count($dates))); - $loops = 0; - // stop after 10 loops for memory reasons. - $timer->start('account-period-loop'); foreach ($dates as $currentDate) { - $title = Navigation::periodShow($currentDate['start'], $currentDate['period']); - [$transactions, $spent] = $this->filterTransactionsByType(TransactionTypeEnum::WITHDRAWAL, $transactions, $currentDate['start'], $currentDate['end']); - [$transactions, $earned] = $this->filterTransactionsByType(TransactionTypeEnum::DEPOSIT, $transactions, $currentDate['start'], $currentDate['end']); - [$transactions, $transferredAway] = $this->filterTransfers('away', $transactions, $currentDate['start'], $currentDate['end']); - [$transactions, $transferredIn] = $this->filterTransfers('in', $transactions, $currentDate['start'], $currentDate['end']); - $entries[] - = [ - 'title' => $title, - 'route' => route('accounts.show', [$account->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferredAway) + count($transferredIn), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred_away' => $this->groupByCurrency($transferredAway), - 'transferred_in' => $this->groupByCurrency($transferredIn), - ]; - ++$loops; + $entries[] = $this->getSingleAccountPeriod($account, $currentDate['start'], $currentDate['end']); } - $timer->stop('account-period-loop'); - $cache->store($entries); - $timer->stop('account-period-total'); Log::debug('End of getAccountPeriodOverview()'); return $entries; } + protected function getSingleAccountPeriod(Account $account, Carbon $start, Carbon $end): array + { + Log::debug(sprintf('Now in getSingleAccountPeriod(#%d, %s %s)', $account->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); + $types = ['spent', 'earned', 'transferred_in', 'transferred_away']; + $return = [ + 'title' => Navigation::periodShow($start, $end), + 'route' => route('accounts.show', [$account->id, $start->format('Y-m-d'), $start->format('Y-m-d')]), + 'total_transactions' => 0, + ]; + foreach ($types as $type) { + $set = $this->getSingleAccountPeriodByType($account, $start, $end, $type); + $return['total_transactions'] += $set['count']; + unset($set['count']); + $return[$type] = $set; + } + return $return; + } + + protected function getSingleAccountPeriodByType(Account $account, Carbon $start, Carbon $end, string $type): array + { + Log::debug(sprintf('Now in getSingleAccountPeriodByType(#%d, %s %s, %s)', $account->id, $start->format('Y-m-d'), $end->format('Y-m-d'), $type)); + $statistics = $this->periodStatisticRepo->findPeriodStatistic($account, $start, $end, $type); + + // nothing found, regenerate them. + if (0 === $statistics->count()) { + $transactions = $this->accountRepository->periodCollection($account, $start, $end); + switch ($type) { + default: + throw new FireflyException(sprintf('Cannot deal with account period type %s', $type)); + case 'spent': + $result = $this->filterTransactionsByType(TransactionTypeEnum::WITHDRAWAL, $transactions, $start, $end); + break; + case 'earned': + $result = $this->filterTransactionsByType(TransactionTypeEnum::DEPOSIT, $transactions, $start, $end); + break; + case 'transferred_in': + $result = $this->filterTransfers('in', $transactions, $start, $end); + break; + case 'transferred_away': + $result = $this->filterTransfers('away', $transactions, $start, $end); + break; + } + // each result must be grouped by currency, then saved as period statistic. + $grouped = $this->groupByCurrency($result); + + // TODO save as statistic. + $this->saveGroupedAsStatistics($account, $start, $end, $type, $grouped); + return $grouped; + } + $grouped = [ + 'count' => 0, + ]; + /** @var PeriodStatistic $statistic */ + foreach($statistics as $statistic) { + $id = (int) $statistic->transaction_currency_id; + $currency = Amount::getTransactionCurrencyById($id); + $grouped[$id] = [ + 'amount' => (string) $statistic->amount, + 'count' => (int) $statistic->count, + 'currency_id' => $currency->id, + 'currency_name' => $currency->name, + 'currency_code' => $currency->code, + 'currency_symbol' => $currency->symbol, + 'currency_decimal_places' => $currency->decimal_places, + ]; + $grouped['count'] += (int) $statistic->count; + } + return $grouped; + } + + protected function saveGroupedAsStatistics(Account $account, Carbon $start, Carbon $end, string $type, array $array): void + { + unset($array['count']); + foreach ($array as $entry) { + $this->periodStatisticRepo->saveStatistic($account, $entry['currency_id'], $start, $end, $type, $entry['count'], $entry['amount']); + } + } + private function filterTransactionsByType(TransactionTypeEnum $type, array $transactions, Carbon $start, Carbon $end): array { - $result = []; - $filtered = []; + $result = []; /** - * @var int $index + * @var int $index * @var array $item */ foreach ($transactions as $index => $item) { @@ -153,25 +192,21 @@ trait PeriodOverview $result[] = $item; unset($transactions[$index]); } - if (!$fits) { - $filtered[] = $item; - } } - return [$filtered, $result]; + return $result; } private function filterTransfers(string $direction, array $transactions, Carbon $start, Carbon $end): array { - $result = []; - $filtered = []; + $result = []; /** - * @var int $index + * @var int $index * @var array $item */ foreach ($transactions as $index => $item) { - $date = Carbon::parse($item['date']); + $date = Carbon::parse($item['date']); if ($date >= $start && $date <= $end) { if ('away' === $direction && -1 === bccomp((string)$item['amount'], '0')) { $result[] = $item; @@ -184,25 +219,34 @@ trait PeriodOverview continue; } } - $filtered[] = $item; } - return [$filtered, $result]; + return $result; } private function groupByCurrency(array $journals): array { - $return = []; + Log::debug('groupByCurrency()'); + $return = [ + 'count' => 0, + ]; /** @var array $journal */ foreach ($journals as $journal) { - $currencyId = (int)$journal['currency_id']; - $currencyCode = $journal['currency_code']; - $currencyName = $journal['currency_name']; - $currencySymbol = $journal['currency_symbol']; - $currencyDecimalPlaces = $journal['currency_decimal_places']; - $foreignCurrencyId = $journal['foreign_currency_id']; - $amount = $journal['amount'] ?? '0'; + + if (!array_key_exists('currency_id', $journal)) { + Log::debug('very strange!'); + var_dump($journals); + exit; + } + + $currencyId = (int)$journal['currency_id']; + $currencyCode = $journal['currency_code']; + $currencyName = $journal['currency_name']; + $currencySymbol = $journal['currency_symbol']; + $currencyDecimalPlaces = $journal['currency_decimal_places']; + $foreignCurrencyId = $journal['foreign_currency_id']; + $amount = $journal['amount'] ?? '0'; if ($this->convertToPrimary && $currencyId !== $this->primaryCurrency->id && $foreignCurrencyId !== $this->primaryCurrency->id) { $amount = $journal['pc_amount'] ?? '0'; @@ -233,6 +277,7 @@ trait PeriodOverview $return[$currencyId]['amount'] = bcadd($return[$currencyId]['amount'], $amount); ++$return[$currencyId]['count']; + ++$return['count']; } return $return; @@ -245,11 +290,11 @@ trait PeriodOverview */ protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; // properties for entries with their amounts. - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty($range); @@ -261,32 +306,32 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setCategory($category); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setCategory($category); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setCategory($category); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); - $transferSet = $collector->getExtractedJournals(); + $transferSet = $collector->getExtractedJournals(); foreach ($dates as $currentDate) { $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); $earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']); @@ -294,17 +339,17 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'transactions' => 0, - 'title' => $title, - 'route' => route( - 'categories.show', - [$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] - ), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'transactions' => 0, + 'title' => $title, + 'route' => route( + 'categories.show', + [$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] + ), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } $cache->store($entries); @@ -337,11 +382,11 @@ trait PeriodOverview */ protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty($this->convertToPrimary); @@ -352,28 +397,28 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // get all expenses without a budget. /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end)->withoutBudget()->withAccountInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $journals = $collector->getExtractedJournals(); + $journals = $collector->getExtractedJournals(); foreach ($dates as $currentDate) { $set = $this->filterJournalsByDate($journals, $currentDate['start'], $currentDate['end']); $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'title' => $title, - 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($set), - 'spent' => $this->groupByCurrency($set), - 'earned' => [], - 'transferred_away' => [], - 'transferred_in' => [], - ]; + 'title' => $title, + 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($set), + 'spent' => $this->groupByCurrency($set), + 'earned' => [], + 'transferred_away' => [], + 'transferred_in' => [], + ]; } $cache->store($entries); @@ -390,38 +435,38 @@ trait PeriodOverview protected function getNoCategoryPeriodOverview(Carbon $theDate): array { app('log')->debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d'))); - $range = Navigation::getViewRange(true); - $first = $this->journalRepos->firstNull(); - $start = null === $first ? new Carbon() : $first->date; - $end = clone $theDate; - $end = Navigation::endOfPeriod($end, $range); + $range = Navigation::getViewRange(true); + $first = $this->journalRepos->firstNull(); + $start = null === $first ? new Carbon() : $first->date; + $end = clone $theDate; + $end = Navigation::endOfPeriod($end, $range); app('log')->debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d'))); app('log')->debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d'))); // properties for cache - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); @@ -435,13 +480,13 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'title' => $title, - 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'title' => $title, + 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } app('log')->debug('End of loops'); @@ -455,11 +500,11 @@ trait PeriodOverview */ protected function getTagPeriodOverview(Tag $tag, Carbon $start, Carbon $end): array // period overview for tags. { - $range = Navigation::getViewRange(true); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; // properties for cache - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('tag-period-entries'); @@ -469,37 +514,37 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); - $transferSet = $collector->getExtractedJournals(); + $transferSet = $collector->getExtractedJournals(); // filer all of them: - $earnedSet = $this->filterJournalsByTag($earnedSet, $tag); - $spentSet = $this->filterJournalsByTag($spentSet, $tag); - $transferSet = $this->filterJournalsByTag($transferSet, $tag); + $earnedSet = $this->filterJournalsByTag($earnedSet, $tag); + $spentSet = $this->filterJournalsByTag($spentSet, $tag); + $transferSet = $this->filterJournalsByTag($transferSet, $tag); foreach ($dates as $currentDate) { $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); @@ -508,17 +553,17 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'transactions' => 0, - 'title' => $title, - 'route' => route( - 'tags.show', - [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] - ), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'transactions' => 0, + 'title' => $title, + 'route' => route( + 'tags.show', + [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] + ), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } return $entries; @@ -528,7 +573,7 @@ trait PeriodOverview { $return = []; foreach ($set as $entry) { - $found = false; + $found = false; /** @var array $localTag */ foreach ($entry['tags'] as $localTag) { @@ -550,12 +595,12 @@ trait PeriodOverview */ protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); - $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); + $range = Navigation::getViewRange(true); + $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; // properties for cache - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('transactions-period-entries'); @@ -565,16 +610,16 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - $spent = []; - $earned = []; - $transferred = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; + $spent = []; + $earned = []; + $transferred = []; // collect all journals in this period (regardless of type) - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTypes($types)->setRange($start, $end); - $genericSet = $collector->getExtractedJournals(); - $loops = 0; + $genericSet = $collector->getExtractedJournals(); + $loops = 0; foreach ($dates as $currentDate) { $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); @@ -592,14 +637,14 @@ trait PeriodOverview } } $entries[] - = [ - 'title' => $title, - 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + = [ + 'title' => $title, + 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; ++$loops; } diff --git a/database/migrations/2025_09_25_175248_create_period_statistics.php b/database/migrations/2025_09_25_175248_create_period_statistics.php new file mode 100644 index 0000000000..e04afaa9de --- /dev/null +++ b/database/migrations/2025_09_25_175248_create_period_statistics.php @@ -0,0 +1,47 @@ +id(); + $table->timestamps(); + $table->softDeletes(); + $table->integer('primary_statable_id', false, true)->nullable(); + $table->string('primary_statable_type', 255)->nullable(); + + $table->integer('secondary_statable_id', false, true)->nullable(); + $table->string('secondary_statable_type', 255)->nullable(); + + $table->integer('tertiary_statable_id', false, true)->nullable(); + $table->string('tertiary_statable_type', 255)->nullable(); + + $table->integer('transaction_currency_id', false, true); + $table->foreign('transaction_currency_id')->references('id')->on('transaction_currencies')->onDelete('cascade'); + + $table->dateTime('start')->nullable(); + $table->string('start_tz', 50)->nullable(); + $table->dateTime('end')->nullable(); + $table->string('end_tz', 50)->nullable(); + $table->string('type',255); + $table->integer('count', false, true)->default(0); + $table->decimal('amount', 32, 12); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('period_statistics'); + } +}; From 61e8d7d7a2afb096d06933b89f93a6e91c2e94c1 Mon Sep 17 00:00:00 2001 From: JC5 Date: Thu, 25 Sep 2025 19:11:11 +0200 Subject: [PATCH 37/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-09-25?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .ci/php-cs-fixer/composer.lock | 96 ++++++- .../V1/Controllers/Webhook/ShowController.php | 2 +- app/Models/Account.php | 1 - app/Models/PeriodStatistic.php | 8 +- .../PeriodStatisticRepository.php | 24 +- .../PeriodStatisticRepositoryInterface.php | 4 +- .../Http/Controllers/PeriodOverview.php | 247 ++++++++++-------- composer.lock | 62 ++--- config/firefly.php | 4 +- package-lock.json | 115 ++++---- 10 files changed, 332 insertions(+), 231 deletions(-) diff --git a/.ci/php-cs-fixer/composer.lock b/.ci/php-cs-fixer/composer.lock index a7ad9afcb3..18ee8f03de 100644 --- a/.ci/php-cs-fixer/composer.lock +++ b/.ci/php-cs-fixer/composer.lock @@ -402,16 +402,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.87.2", + "version": "v3.88.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992" + "reference": "f23469674ae50d40e398bfff8018911a2a2b0dbe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992", - "reference": "da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/f23469674ae50d40e398bfff8018911a2a2b0dbe", + "reference": "f23469674ae50d40e398bfff8018911a2a2b0dbe", "shasum": "" }, "require": { @@ -438,12 +438,13 @@ "symfony/polyfill-mbstring": "^1.33", "symfony/polyfill-php80": "^1.33", "symfony/polyfill-php81": "^1.33", + "symfony/polyfill-php84": "^1.33", "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2", "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0" }, "require-dev": { "facile-it/paraunit": "^1.3.1 || ^2.7", - "infection/infection": "^0.29.14", + "infection/infection": "^0.31.0", "justinrainbow/json-schema": "^6.5", "keradus/cli-executor": "^2.2", "mikey179/vfsstream": "^1.6.12", @@ -451,7 +452,6 @@ "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", "phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34", - "symfony/polyfill-php84": "^1.33", "symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2", "symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2" }, @@ -494,7 +494,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.87.2" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.88.0" }, "funding": [ { @@ -502,7 +502,7 @@ "type": "github" } ], - "time": "2025-09-10T09:51:40+00:00" + "time": "2025-09-24T21:31:42+00:00" }, { "name": "psr/container", @@ -2283,6 +2283,86 @@ ], "time": "2024-09-09T11:45:10+00:00" }, + { + "name": "symfony/polyfill-php84", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-24T13:30:11+00:00" + }, { "name": "symfony/process", "version": "v7.3.3", diff --git a/app/Api/V1/Controllers/Webhook/ShowController.php b/app/Api/V1/Controllers/Webhook/ShowController.php index a24d5806f4..03249281c3 100644 --- a/app/Api/V1/Controllers/Webhook/ShowController.php +++ b/app/Api/V1/Controllers/Webhook/ShowController.php @@ -160,7 +160,7 @@ class ShowController extends Controller /** @var \FireflyIII\Models\WebhookTrigger $trigger */ - foreach($webhook->webhookTriggers as $trigger) { + foreach ($webhook->webhookTriggers as $trigger) { /** @var MessageGeneratorInterface $engine */ $engine = app(MessageGeneratorInterface::class); $engine->setUser(auth()->user()); diff --git a/app/Models/Account.php b/app/Models/Account.php index 82c6369aff..76d520856d 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -243,7 +243,6 @@ class Account extends Model } public function primaryPeriodStatistics(): MorphMany - { return $this->morphMany(PeriodStatistic::class, 'primary_statable'); diff --git a/app/Models/PeriodStatistic.php b/app/Models/PeriodStatistic.php index 0eb7d1f9b7..030735a00f 100644 --- a/app/Models/PeriodStatistic.php +++ b/app/Models/PeriodStatistic.php @@ -1,5 +1,7 @@ morphTo(); } + public function tertiaryStatable(): MorphTo { @@ -52,5 +54,5 @@ class PeriodStatistic extends Model } - // + } diff --git a/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php index f7665f2b6d..ae8914c0c0 100644 --- a/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php +++ b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php @@ -1,4 +1,6 @@ primaryPeriodStatistics() - ->where('start', $start) - ->where('end', $end) - ->whereIn('type', $types) - ->get(); + ->where('start', $start) + ->where('end', $end) + ->whereIn('type', $types) + ->get() + ; } public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection { return $model->primaryPeriodStatistics() - ->where('start', $start) - ->where('end', $end) - ->where('type', $type) - ->get(); + ->where('start', $start) + ->where('end', $end) + ->where('type', $type) + ->get() + ; } public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic { - $stat = new PeriodStatistic; + $stat = new PeriodStatistic(); $stat->primaryStatable()->associate($model); $stat->transaction_currency_id = $currencyId; $stat->start = $start; @@ -60,6 +63,7 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface $stat->count = $count; $stat->type = $type; $stat->save(); + return $stat; } } diff --git a/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php b/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php index 3d6245022a..0b9a6bfbc0 100644 --- a/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php +++ b/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php @@ -1,4 +1,6 @@ accountRepository = app(AccountRepositoryInterface::class); $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); $range = Navigation::getViewRange(true); - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; Log::debug(sprintf('Count of loops: %d', count($dates))); foreach ($dates as $currentDate) { $entries[] = $this->getSingleAccountPeriod($account, $currentDate['start'], $currentDate['end']); @@ -109,11 +110,12 @@ trait PeriodOverview 'total_transactions' => 0, ]; foreach ($types as $type) { - $set = $this->getSingleAccountPeriodByType($account, $start, $end, $type); + $set = $this->getSingleAccountPeriodByType($account, $start, $end, $type); $return['total_transactions'] += $set['count']; unset($set['count']); $return[$type] = $set; } + return $return; } @@ -125,36 +127,47 @@ trait PeriodOverview // nothing found, regenerate them. if (0 === $statistics->count()) { $transactions = $this->accountRepository->periodCollection($account, $start, $end); + switch ($type) { default: throw new FireflyException(sprintf('Cannot deal with account period type %s', $type)); + case 'spent': $result = $this->filterTransactionsByType(TransactionTypeEnum::WITHDRAWAL, $transactions, $start, $end); + break; + case 'earned': $result = $this->filterTransactionsByType(TransactionTypeEnum::DEPOSIT, $transactions, $start, $end); + break; + case 'transferred_in': $result = $this->filterTransfers('in', $transactions, $start, $end); + break; + case 'transferred_away': $result = $this->filterTransfers('away', $transactions, $start, $end); + break; } // each result must be grouped by currency, then saved as period statistic. - $grouped = $this->groupByCurrency($result); + $grouped = $this->groupByCurrency($result); // TODO save as statistic. $this->saveGroupedAsStatistics($account, $start, $end, $type, $grouped); + return $grouped; } - $grouped = [ + $grouped = [ 'count' => 0, ]; + /** @var PeriodStatistic $statistic */ - foreach($statistics as $statistic) { - $id = (int) $statistic->transaction_currency_id; - $currency = Amount::getTransactionCurrencyById($id); + foreach ($statistics as $statistic) { + $id = (int) $statistic->transaction_currency_id; + $currency = Amount::getTransactionCurrencyById($id); $grouped[$id] = [ 'amount' => (string) $statistic->amount, 'count' => (int) $statistic->count, @@ -166,6 +179,7 @@ trait PeriodOverview ]; $grouped['count'] += (int) $statistic->count; } + return $grouped; } @@ -182,7 +196,7 @@ trait PeriodOverview $result = []; /** - * @var int $index + * @var int $index * @var array $item */ foreach ($transactions as $index => $item) { @@ -202,7 +216,7 @@ trait PeriodOverview $result = []; /** - * @var int $index + * @var int $index * @var array $item */ foreach ($transactions as $index => $item) { @@ -237,16 +251,17 @@ trait PeriodOverview if (!array_key_exists('currency_id', $journal)) { Log::debug('very strange!'); var_dump($journals); + exit; } - $currencyId = (int)$journal['currency_id']; - $currencyCode = $journal['currency_code']; - $currencyName = $journal['currency_name']; - $currencySymbol = $journal['currency_symbol']; - $currencyDecimalPlaces = $journal['currency_decimal_places']; - $foreignCurrencyId = $journal['foreign_currency_id']; - $amount = $journal['amount'] ?? '0'; + $currencyId = (int)$journal['currency_id']; + $currencyCode = $journal['currency_code']; + $currencyName = $journal['currency_name']; + $currencySymbol = $journal['currency_symbol']; + $currencyDecimalPlaces = $journal['currency_decimal_places']; + $foreignCurrencyId = $journal['foreign_currency_id']; + $amount = $journal['amount'] ?? '0'; if ($this->convertToPrimary && $currencyId !== $this->primaryCurrency->id && $foreignCurrencyId !== $this->primaryCurrency->id) { $amount = $journal['pc_amount'] ?? '0'; @@ -290,11 +305,11 @@ trait PeriodOverview */ protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; // properties for entries with their amounts. - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty($range); @@ -306,32 +321,32 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setCategory($category); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setCategory($category); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setCategory($category); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); - $transferSet = $collector->getExtractedJournals(); + $transferSet = $collector->getExtractedJournals(); foreach ($dates as $currentDate) { $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); $earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']); @@ -339,17 +354,17 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'transactions' => 0, - 'title' => $title, - 'route' => route( - 'categories.show', - [$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] - ), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'transactions' => 0, + 'title' => $title, + 'route' => route( + 'categories.show', + [$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] + ), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } $cache->store($entries); @@ -382,11 +397,11 @@ trait PeriodOverview */ protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty($this->convertToPrimary); @@ -397,28 +412,28 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // get all expenses without a budget. /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end)->withoutBudget()->withAccountInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $journals = $collector->getExtractedJournals(); + $journals = $collector->getExtractedJournals(); foreach ($dates as $currentDate) { $set = $this->filterJournalsByDate($journals, $currentDate['start'], $currentDate['end']); $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'title' => $title, - 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($set), - 'spent' => $this->groupByCurrency($set), - 'earned' => [], - 'transferred_away' => [], - 'transferred_in' => [], - ]; + 'title' => $title, + 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($set), + 'spent' => $this->groupByCurrency($set), + 'earned' => [], + 'transferred_away' => [], + 'transferred_in' => [], + ]; } $cache->store($entries); @@ -435,38 +450,38 @@ trait PeriodOverview protected function getNoCategoryPeriodOverview(Carbon $theDate): array { app('log')->debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d'))); - $range = Navigation::getViewRange(true); - $first = $this->journalRepos->firstNull(); - $start = null === $first ? new Carbon() : $first->date; - $end = clone $theDate; - $end = Navigation::endOfPeriod($end, $range); + $range = Navigation::getViewRange(true); + $first = $this->journalRepos->firstNull(); + $start = null === $first ? new Carbon() : $first->date; + $end = clone $theDate; + $end = Navigation::endOfPeriod($end, $range); app('log')->debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d'))); app('log')->debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d'))); // properties for cache - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); @@ -480,13 +495,13 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'title' => $title, - 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'title' => $title, + 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } app('log')->debug('End of loops'); @@ -500,11 +515,11 @@ trait PeriodOverview */ protected function getTagPeriodOverview(Tag $tag, Carbon $start, Carbon $end): array // period overview for tags. { - $range = Navigation::getViewRange(true); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; // properties for cache - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('tag-period-entries'); @@ -514,37 +529,37 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); - $transferSet = $collector->getExtractedJournals(); + $transferSet = $collector->getExtractedJournals(); // filer all of them: - $earnedSet = $this->filterJournalsByTag($earnedSet, $tag); - $spentSet = $this->filterJournalsByTag($spentSet, $tag); - $transferSet = $this->filterJournalsByTag($transferSet, $tag); + $earnedSet = $this->filterJournalsByTag($earnedSet, $tag); + $spentSet = $this->filterJournalsByTag($spentSet, $tag); + $transferSet = $this->filterJournalsByTag($transferSet, $tag); foreach ($dates as $currentDate) { $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); @@ -553,17 +568,17 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'transactions' => 0, - 'title' => $title, - 'route' => route( - 'tags.show', - [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] - ), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'transactions' => 0, + 'title' => $title, + 'route' => route( + 'tags.show', + [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] + ), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } return $entries; @@ -573,7 +588,7 @@ trait PeriodOverview { $return = []; foreach ($set as $entry) { - $found = false; + $found = false; /** @var array $localTag */ foreach ($entry['tags'] as $localTag) { @@ -595,12 +610,12 @@ trait PeriodOverview */ protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); - $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); + $range = Navigation::getViewRange(true); + $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; // properties for cache - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('transactions-period-entries'); @@ -610,16 +625,16 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - $spent = []; - $earned = []; - $transferred = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; + $spent = []; + $earned = []; + $transferred = []; // collect all journals in this period (regardless of type) - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTypes($types)->setRange($start, $end); - $genericSet = $collector->getExtractedJournals(); - $loops = 0; + $genericSet = $collector->getExtractedJournals(); + $loops = 0; foreach ($dates as $currentDate) { $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); @@ -637,14 +652,14 @@ trait PeriodOverview } } $entries[] - = [ - 'title' => $title, - 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + = [ + 'title' => $title, + 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; ++$loops; } diff --git a/composer.lock b/composer.lock index de8828fde5..18cb3ef987 100644 --- a/composer.lock +++ b/composer.lock @@ -4235,16 +4235,16 @@ }, { "name": "paragonie/constant_time_encoding", - "version": "v3.1.1", + "version": "v3.1.3", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", - "reference": "5e9b582660b997de205a84c02a3aac7c060900c9" + "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/5e9b582660b997de205a84c02a3aac7c060900c9", - "reference": "5e9b582660b997de205a84c02a3aac7c060900c9", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", + "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", "shasum": "" }, "require": { @@ -4300,7 +4300,7 @@ "issues": "https://github.com/paragonie/constant_time_encoding/issues", "source": "https://github.com/paragonie/constant_time_encoding" }, - "time": "2025-09-22T21:00:33+00:00" + "time": "2025-09-24T15:06:41+00:00" }, { "name": "paragonie/random_compat", @@ -11406,16 +11406,16 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.28", + "version": "2.1.29", "source": { "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "578fa296a166605d97b94091f724f1257185d278" + "url": "https://github.com/phpstan/phpstan-phar-composer-source.git", + "reference": "git" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/578fa296a166605d97b94091f724f1257185d278", - "reference": "578fa296a166605d97b94091f724f1257185d278", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d618573eed4a1b6b75e37b2e0b65ac65c885d88e", + "reference": "d618573eed4a1b6b75e37b2e0b65ac65c885d88e", "shasum": "" }, "require": { @@ -11460,7 +11460,7 @@ "type": "github" } ], - "time": "2025-09-19T08:58:49+00:00" + "time": "2025-09-25T06:58:18+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -11559,16 +11559,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "12.3.8", + "version": "12.4.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "99e692c6a84708211f7536ba322bbbaef57ac7fc" + "reference": "67e8aed88f93d0e6e1cb7effe1a2dfc2fee6022c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/99e692c6a84708211f7536ba322bbbaef57ac7fc", - "reference": "99e692c6a84708211f7536ba322bbbaef57ac7fc", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/67e8aed88f93d0e6e1cb7effe1a2dfc2fee6022c", + "reference": "67e8aed88f93d0e6e1cb7effe1a2dfc2fee6022c", "shasum": "" }, "require": { @@ -11595,7 +11595,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "12.3.x-dev" + "dev-main": "12.4.x-dev" } }, "autoload": { @@ -11624,7 +11624,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.3.8" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.4.0" }, "funding": [ { @@ -11644,7 +11644,7 @@ "type": "tidelift" } ], - "time": "2025-09-17T11:31:43+00:00" + "time": "2025-09-24T13:44:41+00:00" }, { "name": "phpunit/php-file-iterator", @@ -11893,16 +11893,16 @@ }, { "name": "phpunit/phpunit", - "version": "12.3.13", + "version": "12.3.14", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "44f15312c4968fa8102e491fc6f3746410819c16" + "reference": "13e9b2bea9327b094176147250d2c10319a10f5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/44f15312c4968fa8102e491fc6f3746410819c16", - "reference": "44f15312c4968fa8102e491fc6f3746410819c16", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/13e9b2bea9327b094176147250d2c10319a10f5b", + "reference": "13e9b2bea9327b094176147250d2c10319a10f5b", "shasum": "" }, "require": { @@ -11925,7 +11925,7 @@ "sebastian/comparator": "^7.1.3", "sebastian/diff": "^7.0.0", "sebastian/environment": "^8.0.3", - "sebastian/exporter": "^7.0.1", + "sebastian/exporter": "^7.0.2", "sebastian/global-state": "^8.0.2", "sebastian/object-enumerator": "^7.0.0", "sebastian/type": "^6.0.3", @@ -11970,7 +11970,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.13" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.14" }, "funding": [ { @@ -11994,7 +11994,7 @@ "type": "tidelift" } ], - "time": "2025-09-23T06:25:02+00:00" + "time": "2025-09-24T06:34:27+00:00" }, { "name": "rector/rector", @@ -12420,16 +12420,16 @@ }, { "name": "sebastian/exporter", - "version": "7.0.1", + "version": "7.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "b759164a8e02263784b662889cc6cbb686077af6" + "reference": "016951ae10980765e4e7aee491eb288c64e505b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/b759164a8e02263784b662889cc6cbb686077af6", - "reference": "b759164a8e02263784b662889cc6cbb686077af6", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/016951ae10980765e4e7aee491eb288c64e505b7", + "reference": "016951ae10980765e4e7aee491eb288c64e505b7", "shasum": "" }, "require": { @@ -12486,7 +12486,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.1" + "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.2" }, "funding": [ { @@ -12506,7 +12506,7 @@ "type": "tidelift" } ], - "time": "2025-09-22T05:39:29+00:00" + "time": "2025-09-24T06:16:11+00:00" }, { "name": "sebastian/global-state", diff --git a/config/firefly.php b/config/firefly.php index 4dbb9dfde7..3139bb0be9 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -78,8 +78,8 @@ return [ 'running_balance_column' => env('USE_RUNNING_BALANCE', false), // see cer.php for exchange rates feature flag. ], - 'version' => 'develop/2025-09-23', - 'build_time' => 1758652788, + 'version' => 'develop/2025-09-25', + 'build_time' => 1758820163, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 27, diff --git a/package-lock.json b/package-lock.json index e0ac00f144..d69416dc1e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3281,57 +3281,57 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.21.tgz", - "integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.22.tgz", + "integrity": "sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.3", - "@vue/shared": "3.5.21", + "@babel/parser": "^7.28.4", + "@vue/shared": "3.5.22", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz", - "integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.22.tgz", + "integrity": "sha512-W8RknzUM1BLkypvdz10OVsGxnMAuSIZs9Wdx1vzA3mL5fNMN15rhrSCLiTm6blWeACwUwizzPVqGJgOGBEN/hA==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.21", - "@vue/shared": "3.5.21" + "@vue/compiler-core": "3.5.22", + "@vue/shared": "3.5.22" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz", - "integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.22.tgz", + "integrity": "sha512-tbTR1zKGce4Lj+JLzFXDq36K4vcSZbJ1RBu8FxcDv1IGRz//Dh2EBqksyGVypz3kXpshIfWKGOCcqpSbyGWRJQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.3", - "@vue/compiler-core": "3.5.21", - "@vue/compiler-dom": "3.5.21", - "@vue/compiler-ssr": "3.5.21", - "@vue/shared": "3.5.21", + "@babel/parser": "^7.28.4", + "@vue/compiler-core": "3.5.22", + "@vue/compiler-dom": "3.5.22", + "@vue/compiler-ssr": "3.5.22", + "@vue/shared": "3.5.22", "estree-walker": "^2.0.2", - "magic-string": "^0.30.18", + "magic-string": "^0.30.19", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz", - "integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.22.tgz", + "integrity": "sha512-GdgyLvg4R+7T8Nk2Mlighx7XGxq/fJf9jaVofc3IL0EPesTE86cP/8DD1lT3h1JeZr2ySBvyqKQJgbS54IX1Ww==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.21", - "@vue/shared": "3.5.21" + "@vue/compiler-dom": "3.5.22", + "@vue/shared": "3.5.22" } }, "node_modules/@vue/component-compiler-utils": { @@ -3413,9 +3413,9 @@ "license": "MIT" }, "node_modules/@vue/shared": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.21.tgz", - "integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.22.tgz", + "integrity": "sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==", "dev": true, "license": "MIT" }, @@ -4075,9 +4075,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.6.tgz", - "integrity": "sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==", + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.7.tgz", + "integrity": "sha512-bxxN2M3a4d1CRoQC//IqsR5XrLh0IJ8TCv2x6Y9N0nckNz/rTjZB3//GGscZziZOxmjP55rzxg/ze7usFI9FqQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4329,9 +4329,9 @@ } }, "node_modules/browserify-sign": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.4.tgz", - "integrity": "sha512-pbZw0FHibrwXcpLQlXwHp21A5undDBo+RaGNL0K3KOm8nK8uP6PThhS301VDzoMgURZPiVRWRrVHlo6NyU57kA==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.5.tgz", + "integrity": "sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw==", "dev": true, "license": "ISC", "dependencies": { @@ -4341,12 +4341,12 @@ "create-hmac": "^1.1.7", "elliptic": "^6.6.1", "inherits": "^2.0.4", - "parse-asn1": "^5.1.7", + "parse-asn1": "^5.1.9", "readable-stream": "^2.3.8", "safe-buffer": "^5.2.1" }, "engines": { - "node": ">= 0.12" + "node": ">= 0.10" } }, "node_modules/browserify-zlib": { @@ -4521,9 +4521,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001743", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz", - "integrity": "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==", + "version": "1.0.30001745", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001745.tgz", + "integrity": "sha512-ywt6i8FzvdgrrrGbr1jZVObnVv6adj+0if2/omv9cmR2oiZs30zL4DIyaptKcbOrBdOIc74QTMoJvSE2QHh5UQ==", "dev": true, "funding": [ { @@ -4651,14 +4651,15 @@ } }, "node_modules/cipher-base": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.6.tgz", - "integrity": "sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.7.tgz", + "integrity": "sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==", "dev": true, "license": "MIT", "dependencies": { "inherits": "^2.0.4", - "safe-buffer": "^5.2.1" + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.2" }, "engines": { "node": ">= 0.10" @@ -8760,20 +8761,20 @@ } }, "node_modules/parse-asn1": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.8.tgz", - "integrity": "sha512-e90aVPe/1q/g7BrNeYvbJy++5tln4ShE+I3qZ5LxFpUbu+uavfKMuzH2R3SH141O7Pvruwif0BZRwKoVf6vW6w==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.9.tgz", + "integrity": "sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==", "dev": true, "license": "ISC", "dependencies": { "asn1.js": "^4.10.1", "browserify-aes": "^1.2.0", "evp_bytestokey": "^1.0.3", - "pbkdf2": "^3.1.3", + "pbkdf2": "^3.1.5", "safe-buffer": "^5.2.1" }, "engines": { - "node": ">= 0.12" + "node": ">= 0.10" } }, "node_modules/parse-json": { @@ -8935,9 +8936,9 @@ } }, "node_modules/pbkdf2": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.4.tgz", - "integrity": "sha512-0yPXNT01PxSUdkIxL85Fd+yPdeCcvGwFPAAHbR3Z2ukMERcRrJFfLUKK3oglbQ9eUPeX6qDY3QiELqiDarZYUQ==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.5.tgz", + "integrity": "sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8949,7 +8950,7 @@ "to-buffer": "^1.2.1" }, "engines": { - "node": ">=0.12" + "node": ">= 0.10" } }, "node_modules/picocolors": { @@ -10222,9 +10223,9 @@ "license": "MIT" }, "node_modules/sass": { - "version": "1.93.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.93.1.tgz", - "integrity": "sha512-wLAeLB7IksO2u+cCfhHqcy7/2ZUMPp/X2oV6+LjmweTqgjhOKrkaE/Q1wljxtco5EcOcupZ4c981X0gpk5Tiag==", + "version": "1.93.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.93.2.tgz", + "integrity": "sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg==", "dev": true, "license": "MIT", "dependencies": { @@ -11233,9 +11234,9 @@ "license": "MIT" }, "node_modules/to-buffer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.1.tgz", - "integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", "dev": true, "license": "MIT", "dependencies": { From 74f7c07a76471377ebc3e976dedb03c6fb289888 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 25 Sep 2025 19:17:10 +0200 Subject: [PATCH 38/91] Add filter for transfer type. --- app/Support/Http/Controllers/PeriodOverview.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index 6944eb5f26..1515581961 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -208,12 +208,12 @@ trait PeriodOverview foreach ($transactions as $index => $item) { $date = Carbon::parse($item['date']); if ($date >= $start && $date <= $end) { - if ('away' === $direction && -1 === bccomp((string)$item['amount'], '0')) { + if ('Transfer' === $item['type'] && 'away' === $direction && -1 === bccomp((string)$item['amount'], '0')) { $result[] = $item; continue; } - if ('in' === $direction && 1 === bccomp((string)$item['amount'], '0')) { + if ('Transfer' === $item['type'] && 'in' === $direction && 1 === bccomp((string)$item['amount'], '0')) { $result[] = $item; continue; From 08879d31ba20f25e5a9e05c06bfe0b760b85905e Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 26 Sep 2025 05:33:35 +0200 Subject: [PATCH 39/91] Rearrange some code. --- .../Http/Api/AccountBalanceGrouped.php | 74 ++--- app/Support/Http/Api/CleansChartData.php | 2 +- .../Http/Api/CollectsAccountsFromFilter.php | 2 +- .../Http/Api/ExchangeRateConverter.php | 37 ++- .../Http/Api/SummaryBalanceGrouped.php | 32 +-- .../Http/Api/ValidatesUserGroupTrait.php | 24 +- app/Support/Http/Controllers/AugumentData.php | 56 ++-- .../Http/Controllers/ChartGeneration.php | 32 +-- app/Support/Http/Controllers/CreateStuff.php | 9 +- app/Support/Http/Controllers/CronRunner.php | 52 ++-- .../Http/Controllers/DateCalculation.php | 14 +- .../Http/Controllers/GetConfigurationData.php | 60 ++--- .../Http/Controllers/ModelInformation.php | 46 ++-- .../Http/Controllers/PeriodOverview.php | 254 +++++++++--------- .../Http/Controllers/RenderPartialViews.php | 46 ++-- .../Http/Controllers/RequestInformation.php | 25 +- .../Http/Controllers/RuleManagement.php | 22 +- .../Controllers/TransactionCalculation.php | 26 +- .../Http/Controllers/UserNavigation.php | 10 +- 19 files changed, 407 insertions(+), 416 deletions(-) diff --git a/app/Support/Http/Api/AccountBalanceGrouped.php b/app/Support/Http/Api/AccountBalanceGrouped.php index 839c87bfb0..d1f7915d23 100644 --- a/app/Support/Http/Api/AccountBalanceGrouped.php +++ b/app/Support/Http/Api/AccountBalanceGrouped.php @@ -44,10 +44,10 @@ class AccountBalanceGrouped private readonly ExchangeRateConverter $converter; private array $currencies = []; private array $data = []; - private TransactionCurrency $primary; private Carbon $end; private array $journals = []; private string $preferredRange; + private TransactionCurrency $primary; private Carbon $start; public function __construct() @@ -67,7 +67,7 @@ class AccountBalanceGrouped /** @var array $currency */ foreach ($this->data as $currency) { // income and expense array prepped: - $income = [ + $income = [ 'label' => 'earned', 'currency_id' => (string)$currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], @@ -86,7 +86,7 @@ class AccountBalanceGrouped 'entries' => [], 'pc_entries' => [], ]; - $expense = [ + $expense = [ 'label' => 'spent', 'currency_id' => (string)$currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], @@ -108,22 +108,22 @@ class AccountBalanceGrouped // loop all possible periods between $start and $end, and add them to the correct dataset. $currentStart = clone $this->start; while ($currentStart <= $this->end) { - $key = $currentStart->format($this->carbonFormat); - $label = $currentStart->toAtomString(); + $key = $currentStart->format($this->carbonFormat); + $label = $currentStart->toAtomString(); // normal entries - $income['entries'][$label] = Steam::bcround($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']); - $expense['entries'][$label] = Steam::bcround($currency[$key]['spent'] ?? '0', $currency['currency_decimal_places']); + $income['entries'][$label] = Steam::bcround($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']); + $expense['entries'][$label] = Steam::bcround($currency[$key]['spent'] ?? '0', $currency['currency_decimal_places']); // converted entries $income['pc_entries'][$label] = Steam::bcround($currency[$key]['pc_earned'] ?? '0', $currency['primary_currency_decimal_places']); $expense['pc_entries'][$label] = Steam::bcround($currency[$key]['pc_spent'] ?? '0', $currency['primary_currency_decimal_places']); // next loop - $currentStart = Navigation::addPeriod($currentStart, $this->preferredRange, 0); + $currentStart = Navigation::addPeriod($currentStart, $this->preferredRange, 0); } - $chartData[] = $income; - $chartData[] = $expense; + $chartData[] = $income; + $chartData[] = $expense; } return $chartData; @@ -149,9 +149,9 @@ class AccountBalanceGrouped private function processJournal(array $journal): void { // format the date according to the period - $period = $journal['date']->format($this->carbonFormat); - $currencyId = (int)$journal['currency_id']; - $currency = $this->findCurrency($currencyId); + $period = $journal['date']->format($this->carbonFormat); + $currencyId = (int)$journal['currency_id']; + $currency = $this->findCurrency($currencyId); // set the array with monetary info, if it does not exist. $this->createDefaultDataEntry($journal); @@ -159,12 +159,12 @@ class AccountBalanceGrouped $this->createDefaultPeriodEntry($journal); // is this journal's amount in- our outgoing? - $key = $this->getDataKey($journal); - $amount = 'spent' === $key ? Steam::negative($journal['amount']) : Steam::positive($journal['amount']); + $key = $this->getDataKey($journal); + $amount = 'spent' === $key ? Steam::negative($journal['amount']) : Steam::positive($journal['amount']); // get conversion rate - $rate = $this->getRate($currency, $journal['date']); - $amountConverted = bcmul($amount, $rate); + $rate = $this->getRate($currency, $journal['date']); + $amountConverted = bcmul($amount, $rate); // perhaps transaction already has the foreign amount in the primary currency. if ((int)$journal['foreign_currency_id'] === $this->primary->id) { @@ -173,7 +173,7 @@ class AccountBalanceGrouped } // add normal entry - $this->data[$currencyId][$period][$key] = bcadd((string)$this->data[$currencyId][$period][$key], $amount); + $this->data[$currencyId][$period][$key] = bcadd((string)$this->data[$currencyId][$period][$key], $amount); // add converted entry $convertedKey = sprintf('pc_%s', $key); @@ -192,7 +192,7 @@ class AccountBalanceGrouped private function createDefaultDataEntry(array $journal): void { - $currencyId = (int)$journal['currency_id']; + $currencyId = (int)$journal['currency_id']; $this->data[$currencyId] ??= [ 'currency_id' => (string)$currencyId, 'currency_symbol' => $journal['currency_symbol'], @@ -209,8 +209,8 @@ class AccountBalanceGrouped private function createDefaultPeriodEntry(array $journal): void { - $currencyId = (int)$journal['currency_id']; - $period = $journal['date']->format($this->carbonFormat); + $currencyId = (int)$journal['currency_id']; + $period = $journal['date']->format($this->carbonFormat); $this->data[$currencyId][$period] ??= [ 'period' => $period, 'spent' => '0', @@ -259,6 +259,22 @@ class AccountBalanceGrouped $this->accountIds = $accounts->pluck('id')->toArray(); } + public function setEnd(Carbon $end): void + { + $this->end = $end; + } + + public function setJournals(array $journals): void + { + $this->journals = $journals; + } + + public function setPreferredRange(string $preferredRange): void + { + $this->preferredRange = $preferredRange; + $this->carbonFormat = Navigation::preferredCarbonFormatByPeriod($preferredRange); + } + public function setPrimary(TransactionCurrency $primary): void { $this->primary = $primary; @@ -278,22 +294,6 @@ class AccountBalanceGrouped ]; } - public function setEnd(Carbon $end): void - { - $this->end = $end; - } - - public function setJournals(array $journals): void - { - $this->journals = $journals; - } - - public function setPreferredRange(string $preferredRange): void - { - $this->preferredRange = $preferredRange; - $this->carbonFormat = Navigation::preferredCarbonFormatByPeriod($preferredRange); - } - public function setStart(Carbon $start): void { $this->start = $start; diff --git a/app/Support/Http/Api/CleansChartData.php b/app/Support/Http/Api/CleansChartData.php index aef2ec443f..a3d54974cf 100644 --- a/app/Support/Http/Api/CleansChartData.php +++ b/app/Support/Http/Api/CleansChartData.php @@ -43,7 +43,7 @@ trait CleansChartData $return = []; /** - * @var int $index + * @var int $index * @var array $array */ foreach ($data as $index => $array) { diff --git a/app/Support/Http/Api/CollectsAccountsFromFilter.php b/app/Support/Http/Api/CollectsAccountsFromFilter.php index 7c10a6c1e1..2911ed61f6 100644 --- a/app/Support/Http/Api/CollectsAccountsFromFilter.php +++ b/app/Support/Http/Api/CollectsAccountsFromFilter.php @@ -39,7 +39,7 @@ trait CollectsAccountsFromFilter // always collect from the query parameter, even when it's empty. if (null !== $queryParameters['accounts']) { foreach ($queryParameters['accounts'] as $accountId) { - $account = $this->repository->find((int) $accountId); + $account = $this->repository->find((int)$accountId); if (null !== $account) { $collection->push($account); } diff --git a/app/Support/Http/Api/ExchangeRateConverter.php b/app/Support/Http/Api/ExchangeRateConverter.php index 8abb75fe76..c78b7afce8 100644 --- a/app/Support/Http/Api/ExchangeRateConverter.php +++ b/app/Support/Http/Api/ExchangeRateConverter.php @@ -99,8 +99,8 @@ class ExchangeRateConverter */ private function getRate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string { - $key = $this->getCacheKey($from, $to, $date); - $res = Cache::get($key, null); + $key = $this->getCacheKey($from, $to, $date); + $res = Cache::get($key, null); // find in cache if (null !== $res) { @@ -110,7 +110,7 @@ class ExchangeRateConverter } // find in database - $rate = $this->getFromDB($from->id, $to->id, $date->format('Y-m-d')); + $rate = $this->getFromDB($from->id, $to->id, $date->format('Y-m-d')); if (null !== $rate) { Cache::forever($key, $rate); Log::debug(sprintf('ExchangeRateConverter: Return DB rate from %s to %s on %s.', $from->code, $to->code, $date->format('Y-m-d'))); @@ -119,7 +119,7 @@ class ExchangeRateConverter } // find reverse in database - $rate = $this->getFromDB($to->id, $from->id, $date->format('Y-m-d')); + $rate = $this->getFromDB($to->id, $from->id, $date->format('Y-m-d')); if (null !== $rate) { $rate = bcdiv('1', $rate); Cache::forever($key, $rate); @@ -159,7 +159,7 @@ class ExchangeRateConverter return '1'; } - $key = sprintf('cer-%d-%d-%s', $from, $to, $date); + $key = sprintf('cer-%d-%d-%s', $from, $to, $date); // perhaps the rate has been cached during this particular run $preparedRate = $this->prepared[$date][$from][$to] ?? null; @@ -169,7 +169,7 @@ class ExchangeRateConverter return $preparedRate; } - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($key); if ($cache->has()) { $rate = $cache->get(); @@ -182,15 +182,14 @@ class ExchangeRateConverter } /** @var null|CurrencyExchangeRate $result */ - $result = $this->userGroup->currencyExchangeRates() - ->where('from_currency_id', $from) - ->where('to_currency_id', $to) - ->where('date', '<=', $date) - ->orderBy('date', 'DESC') - ->first() - ; + $result = $this->userGroup->currencyExchangeRates() + ->where('from_currency_id', $from) + ->where('to_currency_id', $to) + ->where('date', '<=', $date) + ->orderBy('date', 'DESC') + ->first(); ++$this->queryCount; - $rate = (string) $result?->rate; + $rate = (string)$result?->rate; if ('' === $rate) { app('log')->debug(sprintf('ExchangeRateConverter: Found no rate for #%d->#%d (%s) in the DB.', $from, $to, $date)); @@ -230,13 +229,13 @@ class ExchangeRateConverter if ($euroId === $currency->id) { return '1'; } - $rate = $this->getFromDB($currency->id, $euroId, $date->format('Y-m-d')); + $rate = $this->getFromDB($currency->id, $euroId, $date->format('Y-m-d')); if (null !== $rate) { // app('log')->debug(sprintf('Rate for %s to EUR is %s.', $currency->code, $rate)); return $rate; } - $rate = $this->getFromDB($euroId, $currency->id, $date->format('Y-m-d')); + $rate = $this->getFromDB($euroId, $currency->id, $date->format('Y-m-d')); if (null !== $rate) { return bcdiv('1', $rate); // app('log')->debug(sprintf('Inverted rate for %s to EUR is %s.', $currency->code, $rate)); @@ -245,7 +244,7 @@ class ExchangeRateConverter // grab backup values from config file: $backup = config(sprintf('cer.rates.%s', $currency->code)); if (null !== $backup) { - return bcdiv('1', (string) $backup); + return bcdiv('1', (string)$backup); // app('log')->debug(sprintf('Backup rate for %s to EUR is %s.', $currency->code, $backup)); // return $backup; } @@ -263,9 +262,9 @@ class ExchangeRateConverter $cache = new CacheProperties(); $cache->addProperty('cer-euro-id'); if ($cache->has()) { - return (int) $cache->get(); + return (int)$cache->get(); } - $euro = Amount::getTransactionCurrencyByCode('EUR'); + $euro = Amount::getTransactionCurrencyByCode('EUR'); ++$this->queryCount; $cache->store($euro->id); diff --git a/app/Support/Http/Api/SummaryBalanceGrouped.php b/app/Support/Http/Api/SummaryBalanceGrouped.php index b50ab2160e..8f88dba7d5 100644 --- a/app/Support/Http/Api/SummaryBalanceGrouped.php +++ b/app/Support/Http/Api/SummaryBalanceGrouped.php @@ -31,7 +31,7 @@ use Illuminate\Support\Facades\Log; class SummaryBalanceGrouped { - private const string SUM = 'sum'; + private const string SUM = 'sum'; private array $amounts = []; private array $currencies; private readonly CurrencyRepositoryInterface $currencyRepository; @@ -48,9 +48,9 @@ class SummaryBalanceGrouped public function groupData(): array { Log::debug('Now going to group data.'); - $return = []; + $return = []; foreach ($this->keys as $key) { - $title = match ($key) { + $title = match ($key) { 'sum' => 'balance', 'expense' => 'spent', 'income' => 'earned', @@ -60,7 +60,7 @@ class SummaryBalanceGrouped $return[] = [ 'key' => sprintf('%s-in-pc', $title), 'value' => $this->amounts[$key]['primary'] ?? '0', - 'currency_id' => (string) $this->default->id, + 'currency_id' => (string)$this->default->id, 'currency_code' => $this->default->code, 'currency_symbol' => $this->default->symbol, 'currency_decimal_places' => $this->default->decimal_places, @@ -73,7 +73,7 @@ class SummaryBalanceGrouped // skip primary entries. continue; } - $currencyId = (int) $currencyId; + $currencyId = (int)$currencyId; $currency = $this->currencies[$currencyId] ?? $this->currencyRepository->find($currencyId); $this->currencies[$currencyId] = $currency; // create objects for big array. @@ -87,7 +87,7 @@ class SummaryBalanceGrouped $return[] = [ 'key' => sprintf('%s-in-%s', $title, $currency->code), 'value' => $this->amounts[$key][$currencyId] ?? '0', - 'currency_id' => (string) $currency->id, + 'currency_id' => (string)$currency->id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, 'currency_decimal_places' => $currency->decimal_places, @@ -109,12 +109,12 @@ class SummaryBalanceGrouped /** @var array $journal */ foreach ($journals as $journal) { // transaction info: - $currencyId = (int) $journal['currency_id']; - $amount = bcmul((string) $journal['amount'], $multiplier); - $currency = $this->currencies[$currencyId] ?? Amount::getTransactionCurrencyById($currencyId); - $this->currencies[$currencyId] = $currency; - $pcAmount = $converter->convert($currency, $this->default, $journal['date'], $amount); - if ((int) $journal['foreign_currency_id'] === $this->default->id) { + $currencyId = (int)$journal['currency_id']; + $amount = bcmul((string)$journal['amount'], $multiplier); + $currency = $this->currencies[$currencyId] ?? Amount::getTransactionCurrencyById($currencyId); + $this->currencies[$currencyId] = $currency; + $pcAmount = $converter->convert($currency, $this->default, $journal['date'], $amount); + if ((int)$journal['foreign_currency_id'] === $this->default->id) { // use foreign amount instead $pcAmount = $journal['foreign_amount']; } @@ -126,10 +126,10 @@ class SummaryBalanceGrouped $this->amounts[self::SUM]['primary'] ??= '0'; // add values: - $this->amounts[$key][$currencyId] = bcadd((string) $this->amounts[$key][$currencyId], $amount); - $this->amounts[self::SUM][$currencyId] = bcadd((string) $this->amounts[self::SUM][$currencyId], $amount); - $this->amounts[$key]['primary'] = bcadd((string) $this->amounts[$key]['primary'], (string) $pcAmount); - $this->amounts[self::SUM]['primary'] = bcadd((string) $this->amounts[self::SUM]['primary'], (string) $pcAmount); + $this->amounts[$key][$currencyId] = bcadd((string)$this->amounts[$key][$currencyId], $amount); + $this->amounts[self::SUM][$currencyId] = bcadd((string)$this->amounts[self::SUM][$currencyId], $amount); + $this->amounts[$key]['primary'] = bcadd((string)$this->amounts[$key]['primary'], (string)$pcAmount); + $this->amounts[self::SUM]['primary'] = bcadd((string)$this->amounts[self::SUM]['primary'], (string)$pcAmount); } $converter->summarize(); } diff --git a/app/Support/Http/Api/ValidatesUserGroupTrait.php b/app/Support/Http/Api/ValidatesUserGroupTrait.php index 6ce611396d..0ce08c97ed 100644 --- a/app/Support/Http/Api/ValidatesUserGroupTrait.php +++ b/app/Support/Http/Api/ValidatesUserGroupTrait.php @@ -38,8 +38,8 @@ use Illuminate\Support\Facades\Log; */ trait ValidatesUserGroupTrait { + protected User $user; protected UserGroup $userGroup; - protected User $user; /** * An "undocumented" filter @@ -59,41 +59,41 @@ trait ValidatesUserGroupTrait } /** @var User $user */ - $user = auth()->user(); - $groupId = 0; + $user = auth()->user(); + $groupId = 0; if (!$request->has('user_group_id')) { - $groupId = (int) $user->user_group_id; + $groupId = (int)$user->user_group_id; Log::debug(sprintf('validateUserGroup: no user group submitted, use default group #%d.', $groupId)); } if ($request->has('user_group_id')) { - $groupId = (int) $request->get('user_group_id'); + $groupId = (int)$request->get('user_group_id'); Log::debug(sprintf('validateUserGroup: user group submitted, search for memberships in group #%d.', $groupId)); } /** @var UserGroupRepositoryInterface $repository */ - $repository = app(UserGroupRepositoryInterface::class); + $repository = app(UserGroupRepositoryInterface::class); $repository->setUser($user); $memberships = $repository->getMembershipsFromGroupId($groupId); if (0 === $memberships->count()) { Log::debug(sprintf('validateUserGroup: user has no access to group #%d.', $groupId)); - throw new AuthorizationException((string) trans('validation.no_access_group')); + throw new AuthorizationException((string)trans('validation.no_access_group')); } // need to get the group from the membership: - $group = $repository->getById($groupId); + $group = $repository->getById($groupId); if (null === $group) { Log::debug(sprintf('validateUserGroup: group #%d does not exist.', $groupId)); - throw new AuthorizationException((string) trans('validation.belongs_user_or_user_group')); + throw new AuthorizationException((string)trans('validation.belongs_user_or_user_group')); } Log::debug(sprintf('validateUserGroup: validate access of user to group #%d ("%s").', $groupId, $group->title)); - $roles = property_exists($this, 'acceptedRoles') ? $this->acceptedRoles : []; // @phpstan-ignore-line + $roles = property_exists($this, 'acceptedRoles') ? $this->acceptedRoles : []; // @phpstan-ignore-line if (0 === count($roles)) { Log::debug('validateUserGroup: no roles defined, so no access.'); - throw new AuthorizationException((string) trans('validation.no_accepted_roles_defined')); + throw new AuthorizationException((string)trans('validation.no_accepted_roles_defined')); } Log::debug(sprintf('validateUserGroup: have %d roles to check.', count($roles)), $roles); @@ -111,6 +111,6 @@ trait ValidatesUserGroupTrait Log::debug('validateUserGroup: User does NOT have enough rights to access endpoint.'); - throw new AuthorizationException((string) trans('validation.belongs_user_or_user_group')); + throw new AuthorizationException((string)trans('validation.belongs_user_or_user_group')); } } diff --git a/app/Support/Http/Controllers/AugumentData.php b/app/Support/Http/Controllers/AugumentData.php index d882ce2b5b..aa3883244f 100644 --- a/app/Support/Http/Controllers/AugumentData.php +++ b/app/Support/Http/Controllers/AugumentData.php @@ -56,10 +56,10 @@ trait AugumentData /** @var Account $expenseAccount */ foreach ($accounts as $expenseAccount) { - $collection = new Collection(); + $collection = new Collection(); $collection->push($expenseAccount); - $revenue = $repository->findByName($expenseAccount->name, [AccountTypeEnum::REVENUE->value]); + $revenue = $repository->findByName($expenseAccount->name, [AccountTypeEnum::REVENUE->value]); if (null !== $revenue) { $collection->push($revenue); } @@ -110,13 +110,13 @@ trait AugumentData $grouped = $accounts->groupBy('id')->toArray(); $return = []; foreach ($accountIds as $combinedId) { - $parts = explode('-', (string) $combinedId); - $accountId = (int) $parts[0]; + $parts = explode('-', (string)$combinedId); + $accountId = (int)$parts[0]; if (array_key_exists($accountId, $grouped)) { $return[$accountId] = $grouped[$accountId][0]['name']; } } - $return[0] = '(no name)'; + $return[0] = '(no name)'; return $return; } @@ -136,7 +136,7 @@ trait AugumentData $return[$budgetId] = $grouped[$budgetId][0]['name']; } } - $return[0] = (string) trans('firefly.no_budget'); + $return[0] = (string)trans('firefly.no_budget'); return $return; } @@ -152,13 +152,13 @@ trait AugumentData $grouped = $categories->groupBy('id')->toArray(); $return = []; foreach ($categoryIds as $combinedId) { - $parts = explode('-', (string) $combinedId); - $categoryId = (int) $parts[0]; + $parts = explode('-', (string)$combinedId); + $categoryId = (int)$parts[0]; if (array_key_exists($categoryId, $grouped)) { $return[$categoryId] = $grouped[$categoryId][0]['name']; } } - $return[0] = (string) trans('firefly.no_category'); + $return[0] = (string)trans('firefly.no_category'); return $return; } @@ -171,14 +171,14 @@ trait AugumentData Log::debug('In getLimits'); /** @var OperationsRepositoryInterface $opsRepository */ - $opsRepository = app(OperationsRepositoryInterface::class); + $opsRepository = app(OperationsRepositoryInterface::class); /** @var BudgetLimitRepositoryInterface $blRepository */ - $blRepository = app(BudgetLimitRepositoryInterface::class); + $blRepository = app(BudgetLimitRepositoryInterface::class); $end->endOfMonth(); // properties for cache - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty($budget->id); @@ -189,25 +189,25 @@ trait AugumentData return $cache->get(); } - $set = $blRepository->getBudgetLimits($budget, $start, $end); + $set = $blRepository->getBudgetLimits($budget, $start, $end); $budgetCollection = new Collection()->push($budget); // merge sets based on a key, in case of convert to primary currency - $limits = new Collection(); + $limits = new Collection(); /** @var BudgetLimit $entry */ foreach ($set as $entry) { Log::debug(sprintf('Now at budget limit #%d', $entry->id)); - $currency = $entry->transactionCurrency; + $currency = $entry->transactionCurrency; if ($this->convertToPrimary) { // the sumExpenses method already handles this. $currency = $this->primaryCurrency; } // 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; @@ -219,9 +219,9 @@ trait AugumentData $entry->pc_spent = $spent; // normal amount: - $expenses = $opsRepository->sumExpenses($currentStart, $currentEnd, null, $budgetCollection, $entry->transactionCurrency, false); - $spent = $expenses[$entry->transactionCurrency->id]['sum'] ?? '0'; - $entry->spent = $spent; + $expenses = $opsRepository->sumExpenses($currentStart, $currentEnd, null, $budgetCollection, $entry->transactionCurrency, false); + $spent = $expenses[$entry->transactionCurrency->id]['sum'] ?? '0'; + $entry->spent = $spent; $limits->push($entry); } @@ -240,7 +240,7 @@ trait AugumentData /** @var array $journal */ foreach ($array as $journal) { - $name = '(no name)'; + $name = '(no name)'; if (TransactionTypeEnum::WITHDRAWAL->value === $journal['transaction_type_type']) { $name = $journal['destination_account_name']; } @@ -249,7 +249,7 @@ trait AugumentData } $grouped[$name] ??= '0'; - $grouped[$name] = bcadd((string) $journal['amount'], $grouped[$name]); + $grouped[$name] = bcadd((string)$journal['amount'], $grouped[$name]); } return $grouped; @@ -263,16 +263,16 @@ trait AugumentData /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); - $total = $assets->merge($opposing); + $total = $assets->merge($opposing); $collector->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value])->setAccounts($total); - $journals = $collector->getExtractedJournals(); - $sum = [ + $journals = $collector->getExtractedJournals(); + $sum = [ 'grand_sum' => '0', 'per_currency' => [], ]; // loop to support multi currency foreach ($journals as $journal) { - $currencyId = (int) $journal['currency_id']; + $currencyId = (int)$journal['currency_id']; // if not set, set to zero: if (!array_key_exists($currencyId, $sum['per_currency'])) { @@ -287,8 +287,8 @@ trait AugumentData } // add amount - $sum['per_currency'][$currencyId]['sum'] = bcadd($sum['per_currency'][$currencyId]['sum'], (string) $journal['amount']); - $sum['grand_sum'] = bcadd($sum['grand_sum'], (string) $journal['amount']); + $sum['per_currency'][$currencyId]['sum'] = bcadd($sum['per_currency'][$currencyId]['sum'], (string)$journal['amount']); + $sum['grand_sum'] = bcadd($sum['grand_sum'], (string)$journal['amount']); } return $sum; diff --git a/app/Support/Http/Controllers/ChartGeneration.php b/app/Support/Http/Controllers/ChartGeneration.php index 47a85c5c83..7fe3b3ab96 100644 --- a/app/Support/Http/Controllers/ChartGeneration.php +++ b/app/Support/Http/Controllers/ChartGeneration.php @@ -59,28 +59,28 @@ trait ChartGeneration return $cache->get(); } Log::debug('Regenerate chart.account.account-balance-chart from scratch.'); - $locale = app('steam')->getLocale(); + $locale = app('steam')->getLocale(); /** @var GeneratorInterface $generator */ - $generator = app(GeneratorInterface::class); + $generator = app(GeneratorInterface::class); /** @var AccountRepositoryInterface $accountRepos */ - $accountRepos = app(AccountRepositoryInterface::class); + $accountRepos = app(AccountRepositoryInterface::class); - $primary = app('amount')->getPrimaryCurrency(); - $chartData = []; + $primary = app('amount')->getPrimaryCurrency(); + $chartData = []; Log::debug(sprintf('Start of accountBalanceChart(list, %s, %s)', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); /** @var Account $account */ foreach ($accounts as $account) { Log::debug(sprintf('Now at account #%d ("%s)', $account->id, $account->name)); - $currency = $accountRepos->getAccountCurrency($account) ?? $primary; - $usePrimary = $convertToPrimary && $primary->id !== $currency->id; - $field = $convertToPrimary ? 'pc_balance' : 'balance'; - $currency = $usePrimary ? $primary : $currency; + $currency = $accountRepos->getAccountCurrency($account) ?? $primary; + $usePrimary = $convertToPrimary && $primary->id !== $currency->id; + $field = $convertToPrimary ? 'pc_balance' : 'balance'; + $currency = $usePrimary ? $primary : $currency; Log::debug(sprintf('Will use field %s', $field)); - $currentSet = [ + $currentSet = [ 'label' => $account->name, 'currency_symbol' => $currency->symbol, 'entries' => [], @@ -91,16 +91,16 @@ trait ChartGeneration $previous = array_values($range)[0]; Log::debug(sprintf('Start balance for account #%d ("%s) is', $account->id, $account->name), $previous); while ($currentStart <= $end) { - $format = $currentStart->format('Y-m-d'); - $label = trim($currentStart->isoFormat((string) trans('config.month_and_day_js', [], $locale))); - $balance = $range[$format] ?? $previous; - $previous = $balance; + $format = $currentStart->format('Y-m-d'); + $label = trim($currentStart->isoFormat((string)trans('config.month_and_day_js', [], $locale))); + $balance = $range[$format] ?? $previous; + $previous = $balance; $currentStart->addDay(); $currentSet['entries'][$label] = $balance[$field] ?? '0'; } - $chartData[] = $currentSet; + $chartData[] = $currentSet; } - $data = $generator->multiSet($chartData); + $data = $generator->multiSet($chartData); $cache->store($data); return $data; diff --git a/app/Support/Http/Controllers/CreateStuff.php b/app/Support/Http/Controllers/CreateStuff.php index 6ce33985d3..c54d585328 100644 --- a/app/Support/Http/Controllers/CreateStuff.php +++ b/app/Support/Http/Controllers/CreateStuff.php @@ -32,7 +32,6 @@ use FireflyIII\User; use Illuminate\Support\Facades\Log; use Laravel\Passport\Passport; use phpseclib3\Crypt\RSA; - use function Safe\file_put_contents; /** @@ -73,7 +72,7 @@ trait CreateStuff /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); $assetAccount = [ - 'name' => (string) trans('firefly.cash_wallet', [], $language), + 'name' => (string)trans('firefly.cash_wallet', [], $language), 'iban' => null, 'account_type_name' => 'asset', 'virtual_balance' => 0, @@ -104,11 +103,11 @@ trait CreateStuff return; } - $key = RSA::createKey(4096); + $key = RSA::createKey(4096); Log::alert('NO OAuth keys were found. They have been created.'); - file_put_contents($publicKey, (string) $key->getPublicKey()); + file_put_contents($publicKey, (string)$key->getPublicKey()); file_put_contents($privateKey, $key->toString('PKCS1')); } @@ -120,7 +119,7 @@ trait CreateStuff /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); $savingsAccount = [ - 'name' => (string) trans('firefly.new_savings_account', ['bank_name' => $request->get('bank_name')], $language), + 'name' => (string)trans('firefly.new_savings_account', ['bank_name' => $request->get('bank_name')], $language), 'iban' => null, 'account_type_name' => 'asset', 'account_type_id' => null, diff --git a/app/Support/Http/Controllers/CronRunner.php b/app/Support/Http/Controllers/CronRunner.php index c8cf03cfd6..ed3e950024 100644 --- a/app/Support/Http/Controllers/CronRunner.php +++ b/app/Support/Http/Controllers/CronRunner.php @@ -63,32 +63,6 @@ trait CronRunner ]; } - protected function webhookCronJob(bool $force, Carbon $date): array - { - /** @var WebhookCronjob $webhook */ - $webhook = app(WebhookCronjob::class); - $webhook->setForce($force); - $webhook->setDate($date); - - try { - $webhook->fire(); - } catch (FireflyException $e) { - return [ - 'job_fired' => false, - 'job_succeeded' => false, - 'job_errored' => true, - 'message' => $e->getMessage(), - ]; - } - - return [ - 'job_fired' => $webhook->jobFired, - 'job_succeeded' => $webhook->jobSucceeded, - 'job_errored' => $webhook->jobErrored, - 'message' => $webhook->message, - ]; - } - protected function exchangeRatesCronJob(bool $force, Carbon $date): array { /** @var ExchangeRatesCronjob $exchangeRates */ @@ -166,4 +140,30 @@ trait CronRunner 'message' => $recurring->message, ]; } + + protected function webhookCronJob(bool $force, Carbon $date): array + { + /** @var WebhookCronjob $webhook */ + $webhook = app(WebhookCronjob::class); + $webhook->setForce($force); + $webhook->setDate($date); + + try { + $webhook->fire(); + } catch (FireflyException $e) { + return [ + 'job_fired' => false, + 'job_succeeded' => false, + 'job_errored' => true, + 'message' => $e->getMessage(), + ]; + } + + return [ + 'job_fired' => $webhook->jobFired, + 'job_succeeded' => $webhook->jobSucceeded, + 'job_errored' => $webhook->jobErrored, + 'message' => $webhook->message, + ]; + } } diff --git a/app/Support/Http/Controllers/DateCalculation.php b/app/Support/Http/Controllers/DateCalculation.php index 5027a4c69f..83baefcf8b 100644 --- a/app/Support/Http/Controllers/DateCalculation.php +++ b/app/Support/Http/Controllers/DateCalculation.php @@ -40,13 +40,13 @@ trait DateCalculation */ public function activeDaysLeft(Carbon $start, Carbon $end): int { - $difference = (int) ($start->diffInDays($end, true) + 1); + $difference = (int)($start->diffInDays($end, true) + 1); $today = today(config('app.timezone'))->startOfDay(); if ($start->lte($today) && $end->gte($today)) { $difference = $today->diffInDays($end) + 1; } - return (int) (0 === $difference ? 1 : $difference); + return (int)(0 === $difference ? 1 : $difference); } /** @@ -63,7 +63,7 @@ trait DateCalculation $difference = $start->diffInDays($today, true) + 1; } - return (int) $difference; + return (int)$difference; } protected function calculateStep(Carbon $start, Carbon $end): string @@ -90,19 +90,19 @@ trait DateCalculation protected function getNextPeriods(Carbon $date, string $range): array { // select thing for next 12 periods: - $loop = []; + $loop = []; /** @var Carbon $current */ $current = app('navigation')->startOfPeriod($date, $range); $current = app('navigation')->endOfPeriod($current, $range); $current->addDay(); - $count = 0; + $count = 0; while ($count < 12) { $current = app('navigation')->endOfPeriod($current, $range); $currentStart = app('navigation')->startOfPeriod($current, $range); - $loop[] = [ + $loop[] = [ 'label' => $current->format('Y-m-d'), 'title' => app('navigation')->periodShow($current, $range), 'start' => clone $currentStart, @@ -122,7 +122,7 @@ trait DateCalculation protected function getPreviousPeriods(Carbon $date, string $range): array { // select thing for last 12 periods: - $loop = []; + $loop = []; /** @var Carbon $current */ $current = app('navigation')->startOfPeriod($date, $range); diff --git a/app/Support/Http/Controllers/GetConfigurationData.php b/app/Support/Http/Controllers/GetConfigurationData.php index 52ce494418..d1005bfdcb 100644 --- a/app/Support/Http/Controllers/GetConfigurationData.php +++ b/app/Support/Http/Controllers/GetConfigurationData.php @@ -48,7 +48,7 @@ trait GetConfigurationData E_COMPILE_ERROR | E_RECOVERABLE_ERROR | E_ERROR | E_CORE_ERROR => 'E_COMPILE_ERROR|E_RECOVERABLE_ERROR|E_ERROR|E_CORE_ERROR', ]; - return $array[$value] ?? (string) $value; + return $array[$value] ?? (string)$value; } /** @@ -61,13 +61,13 @@ trait GetConfigurationData $steps = []; if (is_array($elements) && count($elements) > 0) { foreach ($elements as $key => $options) { - $currentStep = $options; + $currentStep = $options; // get the text: - $currentStep['intro'] = (string) trans('intro.'.$route.'_'.$key); + $currentStep['intro'] = (string)trans('intro.' . $route . '_' . $key); // save in array: - $steps[] = $currentStep; + $steps[] = $currentStep; } } app('log')->debug(sprintf('Total basic steps for %s is %d', $routeKey, count($steps))); @@ -82,22 +82,22 @@ trait GetConfigurationData */ protected function getDateRangeConfig(): array // get configuration + get preferences. { - $viewRange = app('navigation')->getViewRange(false); + $viewRange = app('navigation')->getViewRange(false); Log::debug(sprintf('dateRange: the view range is "%s"', $viewRange)); /** @var Carbon $start */ - $start = session('start'); + $start = session('start'); /** @var Carbon $end */ - $end = session('end'); + $end = session('end'); /** @var Carbon $first */ - $first = session('first'); - $title = sprintf('%s - %s', $start->isoFormat($this->monthAndDayFormat), $end->isoFormat($this->monthAndDayFormat)); - $isCustom = true === session('is_custom_range', false); - $today = today(config('app.timezone')); - $ranges = [ + $first = session('first'); + $title = sprintf('%s - %s', $start->isoFormat($this->monthAndDayFormat), $end->isoFormat($this->monthAndDayFormat)); + $isCustom = true === session('is_custom_range', false); + $today = today(config('app.timezone')); + $ranges = [ // first range is the current range: $title => [$start, $end], ]; @@ -127,47 +127,47 @@ trait GetConfigurationData // today: /** @var Carbon $todayStart */ - $todayStart = app('navigation')->startOfPeriod($today, $viewRange); + $todayStart = app('navigation')->startOfPeriod($today, $viewRange); /** @var Carbon $todayEnd */ - $todayEnd = app('navigation')->endOfPeriod($todayStart, $viewRange); + $todayEnd = app('navigation')->endOfPeriod($todayStart, $viewRange); if ($todayStart->ne($start) || $todayEnd->ne($end)) { - $ranges[ucfirst((string) trans('firefly.today'))] = [$todayStart, $todayEnd]; + $ranges[ucfirst((string)trans('firefly.today'))] = [$todayStart, $todayEnd]; } // last seven days: $seven = today(config('app.timezone'))->subDays(7); - $index = (string) trans('firefly.last_seven_days'); + $index = (string)trans('firefly.last_seven_days'); $ranges[$index] = [$seven, new Carbon()]; // last 30 days: $thirty = today(config('app.timezone'))->subDays(30); - $index = (string) trans('firefly.last_thirty_days'); + $index = (string)trans('firefly.last_thirty_days'); $ranges[$index] = [$thirty, new Carbon()]; // month to date: $monthBegin = today(config('app.timezone'))->startOfMonth(); - $index = (string) trans('firefly.month_to_date'); + $index = (string)trans('firefly.month_to_date'); $ranges[$index] = [$monthBegin, new Carbon()]; // year to date: $yearBegin = today(config('app.timezone'))->startOfYear(); - $index = (string) trans('firefly.year_to_date'); + $index = (string)trans('firefly.year_to_date'); $ranges[$index] = [$yearBegin, new Carbon()]; // everything - $index = (string) trans('firefly.everything'); + $index = (string)trans('firefly.everything'); $ranges[$index] = [$first, new Carbon()]; return [ 'title' => $title, 'configuration' => [ - 'apply' => (string) trans('firefly.apply'), - 'cancel' => (string) trans('firefly.cancel'), - 'from' => (string) trans('firefly.from'), - 'to' => (string) trans('firefly.to'), - 'customRange' => (string) trans('firefly.customRange'), + 'apply' => (string)trans('firefly.apply'), + 'cancel' => (string)trans('firefly.cancel'), + 'from' => (string)trans('firefly.from'), + 'to' => (string)trans('firefly.to'), + 'customRange' => (string)trans('firefly.customRange'), 'start' => $start->format('Y-m-d'), 'end' => $end->format('Y-m-d'), 'ranges' => $ranges, @@ -186,16 +186,16 @@ trait GetConfigurationData // user is on page with specific instructions: if ('' !== $specificPage) { $routeKey = str_replace('.', '_', $route); - $elements = config(sprintf('intro.%s', $routeKey.'_'.$specificPage)); + $elements = config(sprintf('intro.%s', $routeKey . '_' . $specificPage)); if (is_array($elements) && count($elements) > 0) { foreach ($elements as $key => $options) { - $currentStep = $options; + $currentStep = $options; // get the text: - $currentStep['intro'] = (string) trans('intro.'.$route.'_'.$specificPage.'_'.$key); + $currentStep['intro'] = (string)trans('intro.' . $route . '_' . $specificPage . '_' . $key); // save in array: - $steps[] = $currentStep; + $steps[] = $currentStep; } } } @@ -207,7 +207,7 @@ trait GetConfigurationData protected function verifyRecurringCronJob(): void { $config = FireflyConfig::get('last_rt_job', 0); - $lastTime = (int) $config?->data; + $lastTime = (int)$config?->data; $now = Carbon::now()->getTimestamp(); app('log')->debug(sprintf('verifyRecurringCronJob: last time is %d ("%s"), now is %d', $lastTime, $config?->data, $now)); if (0 === $lastTime) { diff --git a/app/Support/Http/Controllers/ModelInformation.php b/app/Support/Http/Controllers/ModelInformation.php index 65fd660211..152c9671ef 100644 --- a/app/Support/Http/Controllers/ModelInformation.php +++ b/app/Support/Http/Controllers/ModelInformation.php @@ -75,21 +75,21 @@ trait ModelInformation protected function getLiabilityTypes(): array { /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class); + $repository = app(AccountRepositoryInterface::class); // types of liability: /** @var AccountType $debt */ - $debt = $repository->getAccountTypeByType(AccountTypeEnum::DEBT->value); + $debt = $repository->getAccountTypeByType(AccountTypeEnum::DEBT->value); /** @var AccountType $loan */ - $loan = $repository->getAccountTypeByType(AccountTypeEnum::LOAN->value); + $loan = $repository->getAccountTypeByType(AccountTypeEnum::LOAN->value); /** @var AccountType $mortgage */ $mortgage = $repository->getAccountTypeByType(AccountTypeEnum::MORTGAGE->value); $liabilityTypes = [ - $debt->id => (string) trans(sprintf('firefly.account_type_%s', AccountTypeEnum::DEBT->value)), - $loan->id => (string) trans(sprintf('firefly.account_type_%s', AccountTypeEnum::LOAN->value)), - $mortgage->id => (string) trans(sprintf('firefly.account_type_%s', AccountTypeEnum::MORTGAGE->value)), + $debt->id => (string)trans(sprintf('firefly.account_type_%s', AccountTypeEnum::DEBT->value)), + $loan->id => (string)trans(sprintf('firefly.account_type_%s', AccountTypeEnum::LOAN->value)), + $mortgage->id => (string)trans(sprintf('firefly.account_type_%s', AccountTypeEnum::MORTGAGE->value)), ]; asort($liabilityTypes); @@ -100,7 +100,7 @@ trait ModelInformation { $roles = []; foreach (config('firefly.accountRoles') as $role) { - $roles[$role] = (string) trans(sprintf('firefly.account_role_%s', $role)); + $roles[$role] = (string)trans(sprintf('firefly.account_role_%s', $role)); } return $roles; @@ -114,11 +114,11 @@ trait ModelInformation protected function getTriggersForBill(Bill $bill): array // get info and argument { // TODO duplicate code - $operators = config('search.operators'); - $triggers = []; + $operators = config('search.operators'); + $triggers = []; foreach ($operators as $key => $operator) { if ('user_action' !== $key && false === $operator['alias']) { - $triggers[$key] = (string) trans(sprintf('firefly.rule_trigger_%s_choice', $key)); + $triggers[$key] = (string)trans(sprintf('firefly.rule_trigger_%s_choice', $key)); } } asort($triggers); @@ -165,27 +165,27 @@ trait ModelInformation private function getTriggersForJournal(TransactionJournal $journal): array { // TODO duplicated code. - $operators = config('search.operators'); - $triggers = []; + $operators = config('search.operators'); + $triggers = []; foreach ($operators as $key => $operator) { if ('user_action' !== $key && false === $operator['alias']) { - $triggers[$key] = (string) trans(sprintf('firefly.rule_trigger_%s_choice', $key)); + $triggers[$key] = (string)trans(sprintf('firefly.rule_trigger_%s_choice', $key)); } } asort($triggers); - $result = []; - $journalTriggers = []; - $values = []; - $index = 0; + $result = []; + $journalTriggers = []; + $values = []; + $index = 0; // amount, description, category, budget, tags, source, destination, notes, currency type // ,type /** @var null|Transaction $source */ - $source = $journal->transactions()->where('amount', '<', 0)->first(); + $source = $journal->transactions()->where('amount', '<', 0)->first(); /** @var null|Transaction $destination */ - $destination = $journal->transactions()->where('amount', '>', 0)->first(); + $destination = $journal->transactions()->where('amount', '>', 0)->first(); if (null === $destination || null === $source) { return $result; } @@ -220,21 +220,21 @@ trait ModelInformation ++$index; // category (if) - $category = $journal->categories()->first(); + $category = $journal->categories()->first(); if (null !== $category) { $journalTriggers[$index] = 'category_is'; $values[$index] = $category->name; ++$index; } // budget (if) - $budget = $journal->budgets()->first(); + $budget = $journal->budgets()->first(); if (null !== $budget) { $journalTriggers[$index] = 'budget_is'; $values[$index] = $budget->name; ++$index; } // tags (if) - $tags = $journal->tags()->get(); + $tags = $journal->tags()->get(); /** @var Tag $tag */ foreach ($tags as $tag) { @@ -243,7 +243,7 @@ trait ModelInformation ++$index; } // notes (if) - $notes = $journal->notes()->first(); + $notes = $journal->notes()->first(); if (null !== $notes) { $journalTriggers[$index] = 'notes_is'; $values[$index] = $notes->text; diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index 52b7a0c240..7371b997bd 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -86,11 +86,11 @@ trait PeriodOverview $this->accountRepository = app(AccountRepositoryInterface::class); $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); $range = Navigation::getViewRange(true); - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; Log::debug(sprintf('Count of loops: %d', count($dates))); foreach ($dates as $currentDate) { $entries[] = $this->getSingleAccountPeriod($account, $currentDate['start'], $currentDate['end']); @@ -110,7 +110,7 @@ trait PeriodOverview 'total_transactions' => 0, ]; foreach ($types as $type) { - $set = $this->getSingleAccountPeriodByType($account, $start, $end, $type); + $set = $this->getSingleAccountPeriodByType($account, $start, $end, $type); $return['total_transactions'] += $set['count']; unset($set['count']); $return[$type] = $set; @@ -153,50 +153,42 @@ trait PeriodOverview break; } // each result must be grouped by currency, then saved as period statistic. - $grouped = $this->groupByCurrency($result); + $grouped = $this->groupByCurrency($result); // TODO save as statistic. $this->saveGroupedAsStatistics($account, $start, $end, $type, $grouped); return $grouped; } - $grouped = [ + $grouped = [ 'count' => 0, ]; /** @var PeriodStatistic $statistic */ foreach ($statistics as $statistic) { - $id = (int) $statistic->transaction_currency_id; - $currency = Amount::getTransactionCurrencyById($id); - $grouped[$id] = [ - 'amount' => (string) $statistic->amount, - 'count' => (int) $statistic->count, + $id = (int)$statistic->transaction_currency_id; + $currency = Amount::getTransactionCurrencyById($id); + $grouped[$id] = [ + 'amount' => (string)$statistic->amount, + 'count' => (int)$statistic->count, 'currency_id' => $currency->id, 'currency_name' => $currency->name, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, 'currency_decimal_places' => $currency->decimal_places, ]; - $grouped['count'] += (int) $statistic->count; + $grouped['count'] += (int)$statistic->count; } return $grouped; } - protected function saveGroupedAsStatistics(Account $account, Carbon $start, Carbon $end, string $type, array $array): void - { - unset($array['count']); - foreach ($array as $entry) { - $this->periodStatisticRepo->saveStatistic($account, $entry['currency_id'], $start, $end, $type, $entry['count'], $entry['amount']); - } - } - private function filterTransactionsByType(TransactionTypeEnum $type, array $transactions, Carbon $start, Carbon $end): array { $result = []; /** - * @var int $index + * @var int $index * @var array $item */ foreach ($transactions as $index => $item) { @@ -216,7 +208,7 @@ trait PeriodOverview $result = []; /** - * @var int $index + * @var int $index * @var array $item */ foreach ($transactions as $index => $item) { @@ -255,13 +247,13 @@ trait PeriodOverview exit; } - $currencyId = (int)$journal['currency_id']; - $currencyCode = $journal['currency_code']; - $currencyName = $journal['currency_name']; - $currencySymbol = $journal['currency_symbol']; - $currencyDecimalPlaces = $journal['currency_decimal_places']; - $foreignCurrencyId = $journal['foreign_currency_id']; - $amount = $journal['amount'] ?? '0'; + $currencyId = (int)$journal['currency_id']; + $currencyCode = $journal['currency_code']; + $currencyName = $journal['currency_name']; + $currencySymbol = $journal['currency_symbol']; + $currencyDecimalPlaces = $journal['currency_decimal_places']; + $foreignCurrencyId = $journal['foreign_currency_id']; + $amount = $journal['amount'] ?? '0'; if ($this->convertToPrimary && $currencyId !== $this->primaryCurrency->id && $foreignCurrencyId !== $this->primaryCurrency->id) { $amount = $journal['pc_amount'] ?? '0'; @@ -298,6 +290,14 @@ trait PeriodOverview return $return; } + protected function saveGroupedAsStatistics(Account $account, Carbon $start, Carbon $end, string $type, array $array): void + { + unset($array['count']); + foreach ($array as $entry) { + $this->periodStatisticRepo->saveStatistic($account, $entry['currency_id'], $start, $end, $type, $entry['count'], $entry['amount']); + } + } + /** * Overview for single category. Has been refactored recently. * @@ -305,11 +305,11 @@ trait PeriodOverview */ protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; // properties for entries with their amounts. - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty($range); @@ -321,32 +321,32 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setCategory($category); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setCategory($category); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setCategory($category); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); - $transferSet = $collector->getExtractedJournals(); + $transferSet = $collector->getExtractedJournals(); foreach ($dates as $currentDate) { $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); $earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']); @@ -354,17 +354,17 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'transactions' => 0, - 'title' => $title, - 'route' => route( - 'categories.show', - [$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] - ), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'transactions' => 0, + 'title' => $title, + 'route' => route( + 'categories.show', + [$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] + ), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } $cache->store($entries); @@ -397,11 +397,11 @@ trait PeriodOverview */ protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty($this->convertToPrimary); @@ -412,28 +412,28 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // get all expenses without a budget. /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end)->withoutBudget()->withAccountInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $journals = $collector->getExtractedJournals(); + $journals = $collector->getExtractedJournals(); foreach ($dates as $currentDate) { $set = $this->filterJournalsByDate($journals, $currentDate['start'], $currentDate['end']); $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'title' => $title, - 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($set), - 'spent' => $this->groupByCurrency($set), - 'earned' => [], - 'transferred_away' => [], - 'transferred_in' => [], - ]; + 'title' => $title, + 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($set), + 'spent' => $this->groupByCurrency($set), + 'earned' => [], + 'transferred_away' => [], + 'transferred_in' => [], + ]; } $cache->store($entries); @@ -450,38 +450,38 @@ trait PeriodOverview protected function getNoCategoryPeriodOverview(Carbon $theDate): array { app('log')->debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d'))); - $range = Navigation::getViewRange(true); - $first = $this->journalRepos->firstNull(); - $start = null === $first ? new Carbon() : $first->date; - $end = clone $theDate; - $end = Navigation::endOfPeriod($end, $range); + $range = Navigation::getViewRange(true); + $first = $this->journalRepos->firstNull(); + $start = null === $first ? new Carbon() : $first->date; + $end = clone $theDate; + $end = Navigation::endOfPeriod($end, $range); app('log')->debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d'))); app('log')->debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d'))); // properties for cache - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); @@ -495,13 +495,13 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'title' => $title, - 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'title' => $title, + 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } app('log')->debug('End of loops'); @@ -515,11 +515,11 @@ trait PeriodOverview */ protected function getTagPeriodOverview(Tag $tag, Carbon $start, Carbon $end): array // period overview for tags. { - $range = Navigation::getViewRange(true); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; // properties for cache - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('tag-period-entries'); @@ -529,37 +529,37 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); - $transferSet = $collector->getExtractedJournals(); + $transferSet = $collector->getExtractedJournals(); // filer all of them: - $earnedSet = $this->filterJournalsByTag($earnedSet, $tag); - $spentSet = $this->filterJournalsByTag($spentSet, $tag); - $transferSet = $this->filterJournalsByTag($transferSet, $tag); + $earnedSet = $this->filterJournalsByTag($earnedSet, $tag); + $spentSet = $this->filterJournalsByTag($spentSet, $tag); + $transferSet = $this->filterJournalsByTag($transferSet, $tag); foreach ($dates as $currentDate) { $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); @@ -568,17 +568,17 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'transactions' => 0, - 'title' => $title, - 'route' => route( - 'tags.show', - [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] - ), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'transactions' => 0, + 'title' => $title, + 'route' => route( + 'tags.show', + [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] + ), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } return $entries; @@ -588,7 +588,7 @@ trait PeriodOverview { $return = []; foreach ($set as $entry) { - $found = false; + $found = false; /** @var array $localTag */ foreach ($entry['tags'] as $localTag) { @@ -610,12 +610,12 @@ trait PeriodOverview */ protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); - $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); + $range = Navigation::getViewRange(true); + $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; // properties for cache - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('transactions-period-entries'); @@ -625,16 +625,16 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - $spent = []; - $earned = []; - $transferred = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; + $spent = []; + $earned = []; + $transferred = []; // collect all journals in this period (regardless of type) - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTypes($types)->setRange($start, $end); - $genericSet = $collector->getExtractedJournals(); - $loops = 0; + $genericSet = $collector->getExtractedJournals(); + $loops = 0; foreach ($dates as $currentDate) { $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); @@ -652,14 +652,14 @@ trait PeriodOverview } } $entries[] - = [ - 'title' => $title, - 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + = [ + 'title' => $title, + 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; ++$loops; } diff --git a/app/Support/Http/Controllers/RenderPartialViews.php b/app/Support/Http/Controllers/RenderPartialViews.php index 4c5f8348c6..05ff99e38b 100644 --- a/app/Support/Http/Controllers/RenderPartialViews.php +++ b/app/Support/Http/Controllers/RenderPartialViews.php @@ -52,20 +52,20 @@ trait RenderPartialViews protected function budgetEntry(array $attributes): string // generate view for report. { /** @var PopupReportInterface $popupHelper */ - $popupHelper = app(PopupReportInterface::class); + $popupHelper = app(PopupReportInterface::class); /** @var BudgetRepositoryInterface $budgetRepository */ $budgetRepository = app(BudgetRepositoryInterface::class); - $budget = $budgetRepository->find((int) $attributes['budgetId']); + $budget = $budgetRepository->find((int)$attributes['budgetId']); - $accountRepos = app(AccountRepositoryInterface::class); - $account = $accountRepos->find((int) $attributes['accountId']); + $accountRepos = app(AccountRepositoryInterface::class); + $account = $accountRepos->find((int)$attributes['accountId']); if (null === $budget || null === $account) { throw new FireflyException('Could not render popup.report.balance-amount because budget or account is null.'); } - $journals = $popupHelper->balanceForBudget($budget, $account, $attributes); + $journals = $popupHelper->balanceForBudget($budget, $account, $attributes); try { $view = view('popup.report.balance-amount', compact('journals', 'budget', 'account'))->render(); @@ -113,14 +113,14 @@ trait RenderPartialViews $budgetRepository = app(BudgetRepositoryInterface::class); /** @var PopupReportInterface $popupHelper */ - $popupHelper = app(PopupReportInterface::class); + $popupHelper = app(PopupReportInterface::class); - $budget = $budgetRepository->find((int) $attributes['budgetId']); + $budget = $budgetRepository->find((int)$attributes['budgetId']); if (null === $budget) { // transactions without a budget. $budget = new Budget(); } - $journals = $popupHelper->byBudget($budget, $attributes); + $journals = $popupHelper->byBudget($budget, $attributes); try { $view = view('popup.report.budget-spent-amount', compact('journals', 'budget'))->render(); @@ -142,11 +142,11 @@ trait RenderPartialViews protected function categoryEntry(array $attributes): string // generate view for report. { /** @var PopupReportInterface $popupHelper */ - $popupHelper = app(PopupReportInterface::class); + $popupHelper = app(PopupReportInterface::class); /** @var CategoryRepositoryInterface $categoryRepository */ $categoryRepository = app(CategoryRepositoryInterface::class); - $category = $categoryRepository->find((int) $attributes['categoryId']); + $category = $categoryRepository->find((int)$attributes['categoryId']); $journals = $popupHelper->byCategory($category, $attributes); try { @@ -237,15 +237,15 @@ trait RenderPartialViews $accountRepository = app(AccountRepositoryInterface::class); /** @var PopupReportInterface $popupHelper */ - $popupHelper = app(PopupReportInterface::class); + $popupHelper = app(PopupReportInterface::class); - $account = $accountRepository->find((int) $attributes['accountId']); + $account = $accountRepository->find((int)$attributes['accountId']); if (null === $account) { return 'This is an unknown account. Apologies.'; } - $journals = $popupHelper->byExpenses($account, $attributes); + $journals = $popupHelper->byExpenses($account, $attributes); try { $view = view('popup.report.expense-entry', compact('journals', 'account'))->render(); @@ -266,8 +266,8 @@ trait RenderPartialViews */ protected function getCurrentActions(Rule $rule): array // get info from object and present. { - $index = 0; - $actions = []; + $index = 0; + $actions = []; // must be repos $currentActions = $rule->ruleActions()->orderBy('order', 'ASC')->get(); @@ -306,11 +306,11 @@ trait RenderPartialViews protected function getCurrentTriggers(Rule $rule): array // get info from object and present. { // TODO duplicated code. - $operators = config('search.operators'); - $triggers = []; + $operators = config('search.operators'); + $triggers = []; foreach ($operators as $key => $operator) { if ('user_action' !== $key && false === $operator['alias']) { - $triggers[$key] = (string) trans(sprintf('firefly.rule_trigger_%s_choice', $key)); + $triggers[$key] = (string)trans(sprintf('firefly.rule_trigger_%s_choice', $key)); } } asort($triggers); @@ -325,7 +325,7 @@ trait RenderPartialViews $count = ($index + 1); try { - $rootOperator = OperatorQuerySearch::getRootOperator((string) $entry->trigger_type); + $rootOperator = OperatorQuerySearch::getRootOperator((string)$entry->trigger_type); if (str_starts_with($rootOperator, '-')) { $rootOperator = substr($rootOperator, 1); } @@ -335,7 +335,7 @@ trait RenderPartialViews 'oldTrigger' => $rootOperator, 'oldValue' => $entry->trigger_value, 'oldChecked' => $entry->stop_processing, - 'oldProhibited' => str_starts_with((string) $entry->trigger_type, '-'), + 'oldProhibited' => str_starts_with((string)$entry->trigger_type, '-'), 'count' => $count, 'triggers' => $triggers, ] @@ -365,14 +365,14 @@ trait RenderPartialViews $accountRepository = app(AccountRepositoryInterface::class); /** @var PopupReportInterface $popupHelper */ - $popupHelper = app(PopupReportInterface::class); - $account = $accountRepository->find((int) $attributes['accountId']); + $popupHelper = app(PopupReportInterface::class); + $account = $accountRepository->find((int)$attributes['accountId']); if (null === $account) { return 'This is an unknown category. Apologies.'; } - $journals = $popupHelper->byIncome($account, $attributes); + $journals = $popupHelper->byIncome($account, $attributes); try { $view = view('popup.report.income-entry', compact('journals', 'account'))->render(); diff --git a/app/Support/Http/Controllers/RequestInformation.php b/app/Support/Http/Controllers/RequestInformation.php index 6c7731b68f..73a39655e6 100644 --- a/app/Support/Http/Controllers/RequestInformation.php +++ b/app/Support/Http/Controllers/RequestInformation.php @@ -32,10 +32,9 @@ use FireflyIII\Support\Binder\AccountList; use FireflyIII\User; use Illuminate\Contracts\Validation\Validator as ValidatorContract; use Illuminate\Routing\Route; -use Illuminate\Support\Facades\Validator; -use Illuminate\Support\Facades\Route as RouteFacade; use Illuminate\Support\Facades\Hash; - +use Illuminate\Support\Facades\Route as RouteFacade; +use Illuminate\Support\Facades\Validator; use function Safe\parse_url; /** @@ -67,7 +66,7 @@ trait RequestInformation 'type' => $triggerInfo['type'] ?? '', 'value' => $triggerInfo['value'] ?? '', 'prohibited' => $triggerInfo['prohibited'] ?? false, - 'stop_processing' => 1 === (int) ($triggerInfo['stop_processing'] ?? '0'), + 'stop_processing' => 1 === (int)($triggerInfo['stop_processing'] ?? '0'), ]; $current = RuleFormRequest::replaceAmountTrigger($current); $triggers[] = $current; @@ -85,13 +84,13 @@ trait RequestInformation $page = $this->getPageName(); $specificPage = $this->getSpecificPageName(); // indicator if user has seen the help for this page ( + special page): - $key = sprintf('shown_demo_%s%s', $page, $specificPage); + $key = sprintf('shown_demo_%s%s', $page, $specificPage); // is there an intro for this route? $intro = config(sprintf('intro.%s', $page)) ?? []; $specialIntro = config(sprintf('intro.%s%s', $page, $specificPage)) ?? []; // some routes have a "what" parameter, which indicates a special page: - $shownDemo = true; + $shownDemo = true; // both must be array and either must be > 0 if (count($intro) > 0 || count($specialIntro) > 0) { $shownDemo = app('preferences')->get($key, false)->data; @@ -128,7 +127,7 @@ trait RequestInformation $start = session('start', today(config('app.timezone'))->startOfMonth()); /** @var Carbon $end */ - $end = session('end', today(config('app.timezone'))->endOfMonth()); + $end = session('end', today(config('app.timezone'))->endOfMonth()); if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) { return true; } @@ -146,20 +145,20 @@ trait RequestInformation final protected function parseAttributes(array $attributes): array // parse input + return result { $attributes['location'] ??= ''; - $attributes['accounts'] = AccountList::routeBinder($attributes['accounts'] ?? '', new Route('get', '', [])); - $date = Carbon::createFromFormat('Ymd', $attributes['startDate']); + $attributes['accounts'] = AccountList::routeBinder($attributes['accounts'] ?? '', new Route('get', '', [])); + $date = Carbon::createFromFormat('Ymd', $attributes['startDate']); if (!$date instanceof Carbon) { $date = today(config('app.timezone')); } $date->startOfMonth(); $attributes['startDate'] = $date; - $date2 = Carbon::createFromFormat('Ymd', $attributes['endDate']); + $date2 = Carbon::createFromFormat('Ymd', $attributes['endDate']); if (!$date2 instanceof Carbon) { $date2 = today(config('app.timezone')); } $date2->endOfDay(); - $attributes['endDate'] = $date2; + $attributes['endDate'] = $date2; return $attributes; } @@ -172,11 +171,11 @@ trait RequestInformation final protected function validatePassword(User $user, string $current, string $new): bool // get request info { if (!Hash::check($current, $user->password)) { - throw new ValidationException((string) trans('firefly.invalid_current_password')); + throw new ValidationException((string)trans('firefly.invalid_current_password')); } if ($current === $new) { - throw new ValidationException((string) trans('firefly.should_change')); + throw new ValidationException((string)trans('firefly.should_change')); } return true; diff --git a/app/Support/Http/Controllers/RuleManagement.php b/app/Support/Http/Controllers/RuleManagement.php index 6b88c3524c..9903873484 100644 --- a/app/Support/Http/Controllers/RuleManagement.php +++ b/app/Support/Http/Controllers/RuleManagement.php @@ -51,7 +51,7 @@ trait RuleManagement [ 'oldAction' => $oldAction['type'], 'oldValue' => $oldAction['value'] ?? '', - 'oldChecked' => 1 === (int) ($oldAction['stop_processing'] ?? '0'), + 'oldChecked' => 1 === (int)($oldAction['stop_processing'] ?? '0'), 'count' => $index + 1, ] )->render(); @@ -74,11 +74,11 @@ trait RuleManagement protected function getPreviousTriggers(Request $request): array { // TODO duplicated code. - $operators = config('search.operators'); - $triggers = []; + $operators = config('search.operators'); + $triggers = []; foreach ($operators as $key => $operator) { if ('user_action' !== $key && false === $operator['alias']) { - $triggers[$key] = (string) trans(sprintf('firefly.rule_trigger_%s_choice', $key)); + $triggers[$key] = (string)trans(sprintf('firefly.rule_trigger_%s_choice', $key)); } } asort($triggers); @@ -94,8 +94,8 @@ trait RuleManagement [ 'oldTrigger' => OperatorQuerySearch::getRootOperator($oldTrigger['type']), 'oldValue' => $oldTrigger['value'] ?? '', - 'oldChecked' => 1 === (int) ($oldTrigger['stop_processing'] ?? '0'), - 'oldProhibited' => 1 === (int) ($oldTrigger['prohibited'] ?? '0'), + 'oldChecked' => 1 === (int)($oldTrigger['stop_processing'] ?? '0'), + 'oldProhibited' => 1 === (int)($oldTrigger['prohibited'] ?? '0'), 'count' => $index + 1, 'triggers' => $triggers, ] @@ -124,15 +124,15 @@ trait RuleManagement $triggers = []; foreach ($operators as $key => $operator) { if ('user_action' !== $key && false === $operator['alias']) { - $triggers[$key] = (string) trans(sprintf('firefly.rule_trigger_%s_choice', $key)); + $triggers[$key] = (string)trans(sprintf('firefly.rule_trigger_%s_choice', $key)); } } asort($triggers); - $index = 0; + $index = 0; foreach ($submittedOperators as $operator) { $rootOperator = OperatorQuerySearch::getRootOperator($operator['type']); - $needsContext = (bool) config(sprintf('search.operators.%s.needs_context', $rootOperator)); + $needsContext = (bool)config(sprintf('search.operators.%s.needs_context', $rootOperator)); try { $renderedEntries[] = view( @@ -164,8 +164,8 @@ trait RuleManagement $repository = app(RuleGroupRepositoryInterface::class); if (0 === $repository->count()) { $data = [ - 'title' => (string) trans('firefly.default_rule_group_name'), - 'description' => (string) trans('firefly.default_rule_group_description'), + 'title' => (string)trans('firefly.default_rule_group_name'), + 'description' => (string)trans('firefly.default_rule_group_description'), 'active' => true, ]; diff --git a/app/Support/Http/Controllers/TransactionCalculation.php b/app/Support/Http/Controllers/TransactionCalculation.php index 17df264ce8..a872b53807 100644 --- a/app/Support/Http/Controllers/TransactionCalculation.php +++ b/app/Support/Http/Controllers/TransactionCalculation.php @@ -39,15 +39,14 @@ trait TransactionCalculation */ protected function getExpensesForOpposing(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): array { - $total = $accounts->merge($opposing); + $total = $accounts->merge($opposing); /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setAccounts($total) - ->setRange($start, $end) - ->withAccountInformation() - ->setTypes([TransactionTypeEnum::WITHDRAWAL->value]) - ; + ->setRange($start, $end) + ->withAccountInformation() + ->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); return $collector->getExtractedJournals(); } @@ -61,8 +60,7 @@ trait TransactionCalculation $collector = app(GroupCollectorInterface::class); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::TRANSFER->value]) - ->setTags($tags)->withAccountInformation() - ; + ->setTags($tags)->withAccountInformation(); return $collector->getExtractedJournals(); } @@ -75,8 +73,7 @@ trait TransactionCalculation /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::TRANSFER->value]) - ->setBudgets($budgets)->withAccountInformation() - ; + ->setBudgets($budgets)->withAccountInformation(); return $collector->getExtractedJournals(); } @@ -93,8 +90,7 @@ trait TransactionCalculation ->setRange($start, $end) ->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::TRANSFER->value]) ->setCategories($categories) - ->withAccountInformation() - ; + ->withAccountInformation(); return $collector->getExtractedJournals(); } @@ -107,8 +103,7 @@ trait TransactionCalculation /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::TRANSFER->value]) - ->setCategories($categories)->withAccountInformation() - ; + ->setCategories($categories)->withAccountInformation(); return $collector->getExtractedJournals(); } @@ -118,7 +113,7 @@ trait TransactionCalculation */ protected function getIncomeForOpposing(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): array { - $total = $accounts->merge($opposing); + $total = $accounts->merge($opposing); /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); @@ -135,8 +130,7 @@ trait TransactionCalculation /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::TRANSFER->value]) - ->setTags($tags)->withAccountInformation() - ; + ->setTags($tags)->withAccountInformation(); return $collector->getExtractedJournals(); } diff --git a/app/Support/Http/Controllers/UserNavigation.php b/app/Support/Http/Controllers/UserNavigation.php index 8fac232ad0..fc34201443 100644 --- a/app/Support/Http/Controllers/UserNavigation.php +++ b/app/Support/Http/Controllers/UserNavigation.php @@ -49,7 +49,7 @@ trait UserNavigation final protected function getPreviousUrl(string $identifier): string { app('log')->debug(sprintf('Trying to retrieve URL stored under "%s"', $identifier)); - $url = (string) session($identifier); + $url = (string)session($identifier); app('log')->debug(sprintf('The URL is %s', $url)); return app('steam')->getSafeUrl($url, route('index')); @@ -69,7 +69,7 @@ trait UserNavigation final protected function isEditableGroup(TransactionGroup $group): bool { /** @var null|TransactionJournal $journal */ - $journal = $group->transactionJournals()->first(); + $journal = $group->transactionJournals()->first(); if (null === $journal) { return false; } @@ -96,10 +96,10 @@ trait UserNavigation return redirect(route('index')); } - $journal = $transaction->transactionJournal; + $journal = $transaction->transactionJournal; /** @var null|Transaction $other */ - $other = $journal->transactions()->where('id', '!=', $transaction->id)->first(); + $other = $journal->transactions()->where('id', '!=', $transaction->id)->first(); if (null === $other) { app('log')->error(sprintf('Account #%d has no valid journals. Dont know where it belongs.', $account->id)); session()->flash('error', trans('firefly.cant_find_redirect_account')); @@ -119,7 +119,7 @@ trait UserNavigation final protected function redirectGroupToAccount(TransactionGroup $group) { /** @var null|TransactionJournal $journal */ - $journal = $group->transactionJournals()->first(); + $journal = $group->transactionJournals()->first(); if (null === $journal) { app('log')->error(sprintf('No journals in group #%d', $group->id)); From 4ec2fcdb8a07e789d93ca54fc57f08dd66ef24d5 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 26 Sep 2025 06:05:37 +0200 Subject: [PATCH 40/91] Optimize queries for statistics. --- .../Controllers/Account/ShowController.php | 2 +- app/Models/PeriodStatistic.php | 3 +- .../PeriodStatisticRepository.php | 5 + .../PeriodStatisticRepositoryInterface.php | 2 + app/Support/Amount.php | 562 ++-- .../Authentication/RemoteUserGuard.php | 72 +- app/Support/Balance.php | 21 +- app/Support/Binder/AccountList.php | 22 +- app/Support/Binder/BudgetList.php | 16 +- app/Support/Binder/CategoryList.php | 12 +- app/Support/Binder/Date.php | 20 +- app/Support/Binder/JournalList.php | 4 +- app/Support/Binder/TagList.php | 11 +- app/Support/Binder/TagOrId.php | 4 +- app/Support/Binder/UserGroupAccount.php | 7 +- app/Support/Binder/UserGroupBill.php | 7 +- app/Support/Binder/UserGroupExchangeRate.php | 7 +- app/Support/Binder/UserGroupTransaction.php | 7 +- app/Support/CacheProperties.php | 29 +- app/Support/Calendar/Calculator.php | 44 +- .../Chart/Budget/FrontpageChartGenerator.php | 138 +- .../Category/FrontpageChartGenerator.php | 61 +- .../Category/WholePeriodChartGenerator.php | 36 +- app/Support/Chart/ChartData.php | 6 +- app/Support/ChartColour.php | 2 +- app/Support/Cronjobs/AutoBudgetCronjob.php | 6 +- app/Support/Cronjobs/BillWarningCronjob.php | 8 +- app/Support/Cronjobs/ExchangeRatesCronjob.php | 6 +- app/Support/Cronjobs/RecurringCronjob.php | 8 +- app/Support/Cronjobs/UpdateCheckCronjob.php | 12 +- app/Support/Cronjobs/WebhookCronjob.php | 6 +- app/Support/Debug/Timer.php | 2 +- app/Support/ExpandedForm.php | 30 +- app/Support/Export/ExportDataGenerator.php | 1400 +++++----- app/Support/FireflyConfig.php | 96 +- app/Support/Form/AccountForm.php | 82 +- app/Support/Form/CurrencyForm.php | 186 +- app/Support/Form/FormSupport.php | 90 +- app/Support/Form/PiggyBankForm.php | 12 +- app/Support/Form/RuleForm.php | 16 +- .../Http/Api/AccountBalanceGrouped.php | 216 +- app/Support/Http/Api/CleansChartData.php | 2 +- .../Http/Api/ExchangeRateConverter.php | 198 +- .../Http/Controllers/PeriodOverview.php | 865 ++++--- .../Http/Controllers/RequestInformation.php | 32 +- .../JsonApi/Enrichments/AccountEnrichment.php | 351 ++- .../Enrichments/AvailableBudgetEnrichment.php | 78 +- .../JsonApi/Enrichments/BudgetEnrichment.php | 98 +- .../Enrichments/BudgetLimitEnrichment.php | 105 +- .../Enrichments/CategoryEnrichment.php | 63 +- .../Enrichments/EnrichmentInterface.php | 2 +- .../Enrichments/PiggyBankEnrichment.php | 194 +- .../Enrichments/PiggyBankEventEnrichment.php | 102 +- .../Enrichments/RecurringEnrichment.php | 654 +++-- .../Enrichments/SubscriptionEnrichment.php | 227 +- .../TransactionGroupEnrichment.php | 246 +- .../JsonApi/Enrichments/WebhookEnrichment.php | 49 +- .../Models/AccountBalanceCalculator.php | 102 +- app/Support/Models/BillDateCalculator.php | 16 +- app/Support/Models/ReturnsIntegerIdTrait.php | 2 +- .../Models/ReturnsIntegerUserIdTrait.php | 4 +- app/Support/Navigation.php | 417 +-- .../RecalculatesAvailableBudgetsTrait.php | 194 +- app/Support/ParseDateString.php | 341 ++- app/Support/Preferences.php | 254 +- .../Report/Budget/BudgetReportGenerator.php | 414 +-- .../Category/CategoryReportGenerator.php | 154 +- .../Summarizer/TransactionSummarizer.php | 48 +- .../Recurring/CalculateRangeOccurrences.php | 16 +- .../Recurring/CalculateXOccurrences.php | 22 +- .../Recurring/CalculateXOccurrencesSince.php | 24 +- .../Recurring/FiltersWeekends.php | 6 +- .../UserGroup/UserGroupInterface.php | 2 +- .../Repositories/UserGroup/UserGroupTrait.php | 11 +- app/Support/Request/AppendsLocationData.php | 186 +- app/Support/Request/ChecksLogin.php | 10 +- app/Support/Request/ConvertsDataTypes.php | 113 +- app/Support/Request/GetRecurrenceData.php | 4 +- app/Support/Request/ValidatesWebhooks.php | 8 +- app/Support/Search/AccountSearch.php | 20 +- app/Support/Search/OperatorQuerySearch.php | 2302 ++++++++--------- .../Search/QueryParser/GdbotsQueryParser.php | 11 +- .../Search/QueryParser/QueryParser.php | 36 +- .../Singleton/PreferencesSingleton.php | 12 +- app/Support/Steam.php | 602 +++-- .../System/GeneratesInstallationId.php | 2 +- app/Support/System/OAuthKeys.php | 145 +- app/Support/Twig/AmountFormat.php | 138 +- app/Support/Twig/General.php | 459 ++-- app/Support/Twig/Rule.php | 60 +- app/Support/Twig/TransactionGroupTwig.php | 332 ++- app/Support/Twig/Translation.php | 4 +- 92 files changed, 6499 insertions(+), 6514 deletions(-) diff --git a/app/Http/Controllers/Account/ShowController.php b/app/Http/Controllers/Account/ShowController.php index 4b77635b4b..8c9ac76727 100644 --- a/app/Http/Controllers/Account/ShowController.php +++ b/app/Http/Controllers/Account/ShowController.php @@ -102,7 +102,7 @@ class ShowController extends Controller // make sure dates are end of day and start of day: $start->startOfDay(); - $end->endOfDay(); + $end->endOfDay()->milli(0); $location = $this->repository->getLocation($account); $attachments = $this->repository->getAttachments($account); diff --git a/app/Models/PeriodStatistic.php b/app/Models/PeriodStatistic.php index 030735a00f..194073bc88 100644 --- a/app/Models/PeriodStatistic.php +++ b/app/Models/PeriodStatistic.php @@ -22,7 +22,8 @@ class PeriodStatistic extends Model 'created_at' => 'datetime', 'updated_at' => 'datetime', 'deleted_at' => 'datetime', - 'date' => SeparateTimezoneCaster::class, + 'start' => SeparateTimezoneCaster::class, + 'end' => SeparateTimezoneCaster::class, ]; } diff --git a/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php index ae8914c0c0..1762329f12 100644 --- a/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php +++ b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php @@ -66,4 +66,9 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface return $stat; } + + public function allInRangeForModel(Model $model, Carbon $start, Carbon $end): Collection + { + return $model->primaryPeriodStatistics()->where('start','>=', $start)->where('end','<=', $end)->get(); + } } diff --git a/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php b/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php index 0b9a6bfbc0..d26d85d101 100644 --- a/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php +++ b/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php @@ -35,4 +35,6 @@ interface PeriodStatisticRepositoryInterface public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection; public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic; + + public function allInRangeForModel(Model $model, Carbon $start, Carbon $end): Collection; } diff --git a/app/Support/Amount.php b/app/Support/Amount.php index c3d82f0caf..4b59c3a4d9 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -41,280 +41,6 @@ use NumberFormatter; */ class Amount { - /** - * This method will properly format the given number, in color or "black and white", - * as a currency, given two things: the currency required and the current locale. - * - * @throws FireflyException - */ - public function formatAnything(TransactionCurrency $format, string $amount, ?bool $coloured = null): string - { - return $this->formatFlat($format->symbol, $format->decimal_places, $amount, $coloured); - } - - /** - * This method will properly format the given number, in color or "black and white", - * as a currency, given two things: the currency required and the current locale. - * - * @throws FireflyException - */ - public function formatFlat(string $symbol, int $decimalPlaces, string $amount, ?bool $coloured = null): string - { - $locale = Steam::getLocale(); - $rounded = Steam::bcround($amount, $decimalPlaces); - $coloured ??= true; - - $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY); - $fmt->setSymbol(NumberFormatter::CURRENCY_SYMBOL, $symbol); - $fmt->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, $decimalPlaces); - $fmt->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, $decimalPlaces); - $result = (string)$fmt->format((float)$rounded); // intentional float - - if (true === $coloured) { - if (1 === bccomp($rounded, '0')) { - return sprintf('%s', $result); - } - if (-1 === bccomp($rounded, '0')) { - return sprintf('%s', $result); - } - - return sprintf('%s', $result); - } - - return $result; - } - - public function formatByCurrencyId(int $currencyId, string $amount, ?bool $coloured = null): string - { - $format = $this->getTransactionCurrencyById($currencyId); - - return $this->formatFlat($format->symbol, $format->decimal_places, $amount, $coloured); - } - - public function getAllCurrencies(): Collection - { - return TransactionCurrency::orderBy('code', 'ASC')->get(); - } - - /** - * Experimental function to see if we can quickly and quietly get the amount from a journal. - * This depends on the user's default currency and the wish to have it converted. - */ - public function getAmountFromJournal(array $journal): string - { - $convertToPrimary = $this->convertToPrimary(); - $currency = $this->getPrimaryCurrency(); - $field = $convertToPrimary && $currency->id !== $journal['currency_id'] ? 'pc_amount' : 'amount'; - $amount = $journal[$field] ?? '0'; - // Log::debug(sprintf('Field is %s, amount is %s', $field, $amount)); - // fallback, the transaction has a foreign amount in $currency. - if ($convertToPrimary && null !== $journal['foreign_amount'] && $currency->id === (int)$journal['foreign_currency_id']) { - $amount = $journal['foreign_amount']; - // Log::debug(sprintf('Overruled, amount is now %s', $amount)); - } - - return (string)$amount; - } - - public function getTransactionCurrencyById(int $currencyId): TransactionCurrency - { - $instance = PreferencesSingleton::getInstance(); - $key = sprintf('transaction_currency_%d', $currencyId); - - /** @var null|TransactionCurrency $pref */ - $pref = $instance->getPreference($key); - if (null !== $pref) { - return $pref; - } - $currency = TransactionCurrency::find($currencyId); - if (null === $currency) { - $message = sprintf('Could not find a transaction currency with ID #%d in %s', $currencyId, __METHOD__); - Log::error($message); - - throw new FireflyException($message); - } - $instance->setPreference($key, $currency); - - return $currency; - } - - public function getTransactionCurrencyByCode(string $code): TransactionCurrency - { - $instance = PreferencesSingleton::getInstance(); - $key = sprintf('transaction_currency_%s', $code); - - /** @var null|TransactionCurrency $pref */ - $pref = $instance->getPreference($key); - if (null !== $pref) { - return $pref; - } - $currency = TransactionCurrency::whereCode($code)->first(); - if (null === $currency) { - $message = sprintf('Could not find a transaction currency with code "%s" in %s', $code, __METHOD__); - Log::error($message); - - throw new FireflyException($message); - } - $instance->setPreference($key, $currency); - - return $currency; - } - - public function convertToPrimary(?User $user = null): bool - { - $instance = PreferencesSingleton::getInstance(); - if (!$user instanceof User) { - $pref = $instance->getPreference('convert_to_primary_no_user'); - if (null === $pref) { - $res = true === Preferences::get('convert_to_primary', false)->data && true === config('cer.enabled'); - $instance->setPreference('convert_to_primary_no_user', $res); - - return $res; - } - - return $pref; - } - $key = sprintf('convert_to_primary_%d', $user->id); - $pref = $instance->getPreference($key); - if (null === $pref) { - $res = true === Preferences::getForUser($user, 'convert_to_primary', false)->data && true === config('cer.enabled'); - $instance->setPreference($key, $res); - - return $res; - } - - return $pref; - } - - public function getPrimaryCurrency(): TransactionCurrency - { - if (auth()->check()) { - /** @var User $user */ - $user = auth()->user(); - if (null !== $user->userGroup) { - return $this->getPrimaryCurrencyByUserGroup($user->userGroup); - } - } - - return $this->getSystemCurrency(); - } - - public function getPrimaryCurrencyByUserGroup(UserGroup $userGroup): TransactionCurrency - { - $cache = new CacheProperties(); - $cache->addProperty('getPrimaryCurrencyByGroup'); - $cache->addProperty($userGroup->id); - if ($cache->has()) { - return $cache->get(); - } - - /** @var null|TransactionCurrency $primary */ - $primary = $userGroup->currencies()->where('group_default', true)->first(); - if (null === $primary) { - $primary = $this->getSystemCurrency(); - // could be the user group has no default right now. - $userGroup->currencies()->sync([$primary->id => ['group_default' => true]]); - } - $cache->store($primary); - - return $primary; - } - - public function getSystemCurrency(): TransactionCurrency - { - return TransactionCurrency::whereNull('deleted_at')->where('code', 'EUR')->first(); - } - - /** - * Experimental function to see if we can quickly and quietly get the amount from a journal. - * This depends on the user's default currency and the wish to have it converted. - */ - public function getAmountFromJournalObject(TransactionJournal $journal): string - { - $convertToPrimary = $this->convertToPrimary(); - $currency = $this->getPrimaryCurrency(); - $field = $convertToPrimary && $currency->id !== $journal->transaction_currency_id ? 'pc_amount' : 'amount'; - - /** @var null|Transaction $sourceTransaction */ - $sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first(); - if (null === $sourceTransaction) { - return '0'; - } - $amount = $sourceTransaction->{$field} ?? '0'; - if ((int)$sourceTransaction->foreign_currency_id === $currency->id) { - // use foreign amount instead! - $amount = (string)$sourceTransaction->foreign_amount; // hard coded to be foreign amount. - } - - return $amount; - } - - public function getCurrencies(): Collection - { - /** @var User $user */ - $user = auth()->user(); - - return $user->currencies()->orderBy('code', 'ASC')->get(); - } - - /** - * This method returns the correct format rules required by accounting.js, - * the library used to format amounts in charts. - * - * Used only in one place. - * - * @throws FireflyException - */ - public function getJsConfig(): array - { - $config = $this->getLocaleInfo(); - $negative = self::getAmountJsConfig($config['n_sep_by_space'], $config['n_sign_posn'], $config['negative_sign'], $config['n_cs_precedes']); - $positive = self::getAmountJsConfig($config['p_sep_by_space'], $config['p_sign_posn'], $config['positive_sign'], $config['p_cs_precedes']); - - return [ - 'mon_decimal_point' => $config['mon_decimal_point'], - 'mon_thousands_sep' => $config['mon_thousands_sep'], - 'format' => [ - 'pos' => $positive, - 'neg' => $negative, - 'zero' => $positive, - ], - ]; - } - - /** - * @throws FireflyException - */ - private function getLocaleInfo(): array - { - // get config from preference, not from translation: - $locale = Steam::getLocale(); - $array = Steam::getLocaleArray($locale); - - setlocale(LC_MONETARY, $array); - $info = localeconv(); - - // correct variables - $info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes'); - $info['p_cs_precedes'] = $this->getLocaleField($info, 'p_cs_precedes'); - - $info['n_sep_by_space'] = $this->getLocaleField($info, 'n_sep_by_space'); - $info['p_sep_by_space'] = $this->getLocaleField($info, 'p_sep_by_space'); - - $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY); - - $info['mon_decimal_point'] = $fmt->getSymbol(NumberFormatter::MONETARY_SEPARATOR_SYMBOL); - $info['mon_thousands_sep'] = $fmt->getSymbol(NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL); - - return $info; - } - - private function getLocaleField(array $info, string $field): bool - { - return (is_bool($info[$field]) && true === $info[$field]) - || (is_int($info[$field]) && 1 === $info[$field]); - } - /** * bool $sepBySpace is $localeconv['n_sep_by_space'] * int $signPosn = $localeconv['n_sign_posn'] @@ -333,11 +59,11 @@ class Amount // there are five possible positions for the "+" or "-" sign (if it is even used) // pos_a and pos_e could be the ( and ) symbol. - $posA = ''; // before everything - $posB = ''; // before currency symbol - $posC = ''; // after currency symbol - $posD = ''; // before amount - $posE = ''; // after everything + $posA = ''; // before everything + $posB = ''; // before currency symbol + $posC = ''; // after currency symbol + $posD = ''; // before amount + $posE = ''; // after everything // format would be (currency before amount) // AB%sC_D%vE @@ -379,9 +105,283 @@ class Amount } if ($csPrecedes) { - return $posA.$posB.'%s'.$posC.$space.$posD.'%v'.$posE; + return $posA . $posB . '%s' . $posC . $space . $posD . '%v' . $posE; } - return $posA.$posD.'%v'.$space.$posB.'%s'.$posC.$posE; + return $posA . $posD . '%v' . $space . $posB . '%s' . $posC . $posE; + } + + public function convertToPrimary(?User $user = null): bool + { + $instance = PreferencesSingleton::getInstance(); + if (!$user instanceof User) { + $pref = $instance->getPreference('convert_to_primary_no_user'); + if (null === $pref) { + $res = true === Preferences::get('convert_to_primary', false)->data && true === config('cer.enabled'); + $instance->setPreference('convert_to_primary_no_user', $res); + + return $res; + } + + return $pref; + } + $key = sprintf('convert_to_primary_%d', $user->id); + $pref = $instance->getPreference($key); + if (null === $pref) { + $res = true === Preferences::getForUser($user, 'convert_to_primary', false)->data && true === config('cer.enabled'); + $instance->setPreference($key, $res); + + return $res; + } + + return $pref; + } + + /** + * This method will properly format the given number, in color or "black and white", + * as a currency, given two things: the currency required and the current locale. + * + * @throws FireflyException + */ + public function formatAnything(TransactionCurrency $format, string $amount, ?bool $coloured = null): string + { + return $this->formatFlat($format->symbol, $format->decimal_places, $amount, $coloured); + } + + public function formatByCurrencyId(int $currencyId, string $amount, ?bool $coloured = null): string + { + $format = $this->getTransactionCurrencyById($currencyId); + + return $this->formatFlat($format->symbol, $format->decimal_places, $amount, $coloured); + } + + /** + * This method will properly format the given number, in color or "black and white", + * as a currency, given two things: the currency required and the current locale. + * + * @throws FireflyException + */ + public function formatFlat(string $symbol, int $decimalPlaces, string $amount, ?bool $coloured = null): string + { + $locale = Steam::getLocale(); + $rounded = Steam::bcround($amount, $decimalPlaces); + $coloured ??= true; + + $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY); + $fmt->setSymbol(NumberFormatter::CURRENCY_SYMBOL, $symbol); + $fmt->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, $decimalPlaces); + $fmt->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, $decimalPlaces); + $result = (string)$fmt->format((float)$rounded); // intentional float + + if (true === $coloured) { + if (1 === bccomp($rounded, '0')) { + return sprintf('%s', $result); + } + if (-1 === bccomp($rounded, '0')) { + return sprintf('%s', $result); + } + + return sprintf('%s', $result); + } + + return $result; + } + + public function getAllCurrencies(): Collection + { + return TransactionCurrency::orderBy('code', 'ASC')->get(); + } + + /** + * Experimental function to see if we can quickly and quietly get the amount from a journal. + * This depends on the user's default currency and the wish to have it converted. + */ + public function getAmountFromJournal(array $journal): string + { + $convertToPrimary = $this->convertToPrimary(); + $currency = $this->getPrimaryCurrency(); + $field = $convertToPrimary && $currency->id !== $journal['currency_id'] ? 'pc_amount' : 'amount'; + $amount = $journal[$field] ?? '0'; + // Log::debug(sprintf('Field is %s, amount is %s', $field, $amount)); + // fallback, the transaction has a foreign amount in $currency. + if ($convertToPrimary && null !== $journal['foreign_amount'] && $currency->id === (int)$journal['foreign_currency_id']) { + $amount = $journal['foreign_amount']; + // Log::debug(sprintf('Overruled, amount is now %s', $amount)); + } + + return (string)$amount; + } + + /** + * Experimental function to see if we can quickly and quietly get the amount from a journal. + * This depends on the user's default currency and the wish to have it converted. + */ + public function getAmountFromJournalObject(TransactionJournal $journal): string + { + $convertToPrimary = $this->convertToPrimary(); + $currency = $this->getPrimaryCurrency(); + $field = $convertToPrimary && $currency->id !== $journal->transaction_currency_id ? 'pc_amount' : 'amount'; + + /** @var null|Transaction $sourceTransaction */ + $sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first(); + if (null === $sourceTransaction) { + return '0'; + } + $amount = $sourceTransaction->{$field} ?? '0'; + if ((int)$sourceTransaction->foreign_currency_id === $currency->id) { + // use foreign amount instead! + $amount = (string)$sourceTransaction->foreign_amount; // hard coded to be foreign amount. + } + + return $amount; + } + + public function getCurrencies(): Collection + { + /** @var User $user */ + $user = auth()->user(); + + return $user->currencies()->orderBy('code', 'ASC')->get(); + } + + /** + * This method returns the correct format rules required by accounting.js, + * the library used to format amounts in charts. + * + * Used only in one place. + * + * @throws FireflyException + */ + public function getJsConfig(): array + { + $config = $this->getLocaleInfo(); + $negative = self::getAmountJsConfig($config['n_sep_by_space'], $config['n_sign_posn'], $config['negative_sign'], $config['n_cs_precedes']); + $positive = self::getAmountJsConfig($config['p_sep_by_space'], $config['p_sign_posn'], $config['positive_sign'], $config['p_cs_precedes']); + + return [ + 'mon_decimal_point' => $config['mon_decimal_point'], + 'mon_thousands_sep' => $config['mon_thousands_sep'], + 'format' => [ + 'pos' => $positive, + 'neg' => $negative, + 'zero' => $positive, + ], + ]; + } + + public function getPrimaryCurrency(): TransactionCurrency + { + if (auth()->check()) { + /** @var User $user */ + $user = auth()->user(); + if (null !== $user->userGroup) { + return $this->getPrimaryCurrencyByUserGroup($user->userGroup); + } + } + + return $this->getSystemCurrency(); + } + + public function getPrimaryCurrencyByUserGroup(UserGroup $userGroup): TransactionCurrency + { + $cache = new CacheProperties(); + $cache->addProperty('getPrimaryCurrencyByGroup'); + $cache->addProperty($userGroup->id); + if ($cache->has()) { + return $cache->get(); + } + + /** @var null|TransactionCurrency $primary */ + $primary = $userGroup->currencies()->where('group_default', true)->first(); + if (null === $primary) { + $primary = $this->getSystemCurrency(); + // could be the user group has no default right now. + $userGroup->currencies()->sync([$primary->id => ['group_default' => true]]); + } + $cache->store($primary); + + return $primary; + } + + public function getSystemCurrency(): TransactionCurrency + { + return TransactionCurrency::whereNull('deleted_at')->where('code', 'EUR')->first(); + } + + public function getTransactionCurrencyByCode(string $code): TransactionCurrency + { + $instance = PreferencesSingleton::getInstance(); + $key = sprintf('transaction_currency_%s', $code); + + /** @var null|TransactionCurrency $pref */ + $pref = $instance->getPreference($key); + if (null !== $pref) { + return $pref; + } + $currency = TransactionCurrency::whereCode($code)->first(); + if (null === $currency) { + $message = sprintf('Could not find a transaction currency with code "%s" in %s', $code, __METHOD__); + Log::error($message); + + throw new FireflyException($message); + } + $instance->setPreference($key, $currency); + + return $currency; + } + + public function getTransactionCurrencyById(int $currencyId): TransactionCurrency + { + $instance = PreferencesSingleton::getInstance(); + $key = sprintf('transaction_currency_%d', $currencyId); + + /** @var null|TransactionCurrency $pref */ + $pref = $instance->getPreference($key); + if (null !== $pref) { + return $pref; + } + $currency = TransactionCurrency::find($currencyId); + if (null === $currency) { + $message = sprintf('Could not find a transaction currency with ID #%d in %s', $currencyId, __METHOD__); + Log::error($message); + + throw new FireflyException($message); + } + $instance->setPreference($key, $currency); + + return $currency; + } + + private function getLocaleField(array $info, string $field): bool + { + return (is_bool($info[$field]) && true === $info[$field]) + || (is_int($info[$field]) && 1 === $info[$field]); + } + + /** + * @throws FireflyException + */ + private function getLocaleInfo(): array + { + // get config from preference, not from translation: + $locale = Steam::getLocale(); + $array = Steam::getLocaleArray($locale); + + setlocale(LC_MONETARY, $array); + $info = localeconv(); + + // correct variables + $info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes'); + $info['p_cs_precedes'] = $this->getLocaleField($info, 'p_cs_precedes'); + + $info['n_sep_by_space'] = $this->getLocaleField($info, 'n_sep_by_space'); + $info['p_sep_by_space'] = $this->getLocaleField($info, 'p_sep_by_space'); + + $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY); + + $info['mon_decimal_point'] = $fmt->getSymbol(NumberFormatter::MONETARY_SEPARATOR_SYMBOL); + $info['mon_thousands_sep'] = $fmt->getSymbol(NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL); + + return $info; } } diff --git a/app/Support/Authentication/RemoteUserGuard.php b/app/Support/Authentication/RemoteUserGuard.php index c2e534184b..353765af94 100644 --- a/app/Support/Authentication/RemoteUserGuard.php +++ b/app/Support/Authentication/RemoteUserGuard.php @@ -48,7 +48,7 @@ class RemoteUserGuard implements Guard public function __construct(protected UserProvider $provider, Application $app) { /** @var null|Request $request */ - $request = $app->get('request'); + $request = $app->get('request'); Log::debug(sprintf('Created RemoteUserGuard for %s "%s"', $request?->getMethod(), $request?->getRequestUri())); $this->application = $app; $this->user = null; @@ -63,8 +63,8 @@ class RemoteUserGuard implements Guard return; } // Get the user identifier from $_SERVER or apache filtered headers - $header = config('auth.guard_header', 'REMOTE_USER'); - $userID = request()->server($header) ?? null; + $header = config('auth.guard_header', 'REMOTE_USER'); + $userID = request()->server($header) ?? null; if (function_exists('apache_request_headers')) { Log::debug('Use apache_request_headers to find user ID.'); @@ -83,10 +83,10 @@ class RemoteUserGuard implements Guard $retrievedUser = $this->provider->retrieveById($userID); // store email address if present in header and not already set. - $header = config('auth.guard_email'); + $header = config('auth.guard_email'); if (null !== $header) { - $emailAddress = (string) (request()->server($header) ?? apache_request_headers()[$header] ?? null); + $emailAddress = (string)(request()->server($header) ?? apache_request_headers()[$header] ?? null); $preference = Preferences::getForUser($retrievedUser, 'remote_guard_alt_email'); if ('' !== $emailAddress && null === $preference && $emailAddress !== $userID) { @@ -99,7 +99,14 @@ class RemoteUserGuard implements Guard } Log::debug(sprintf('Result of getting user from provider: %s', $retrievedUser->email)); - $this->user = $retrievedUser; + $this->user = $retrievedUser; + } + + public function check(): bool + { + Log::debug(sprintf('Now at %s', __METHOD__)); + + return $this->user() instanceof User; } public function guest(): bool @@ -109,11 +116,32 @@ class RemoteUserGuard implements Guard return !$this->check(); } - public function check(): bool + public function hasUser(): bool { Log::debug(sprintf('Now at %s', __METHOD__)); - return $this->user() instanceof User; + throw new FireflyException('Did not implement RemoteUserGuard::hasUser()'); + } + + /** + * @SuppressWarnings("PHPMD.ShortMethodName") + */ + public function id(): int | string | null + { + Log::debug(sprintf('Now at %s', __METHOD__)); + + return $this->user?->id; + } + + public function setUser(Authenticatable | User | null $user): void // @phpstan-ignore-line + { + Log::debug(sprintf('Now at %s', __METHOD__)); + if ($user instanceof User) { + $this->user = $user; + + return; + } + Log::error(sprintf('Did not set user at %s', __METHOD__)); } public function user(): ?User @@ -129,34 +157,6 @@ class RemoteUserGuard implements Guard return $user; } - public function hasUser(): bool - { - Log::debug(sprintf('Now at %s', __METHOD__)); - - throw new FireflyException('Did not implement RemoteUserGuard::hasUser()'); - } - - /** - * @SuppressWarnings("PHPMD.ShortMethodName") - */ - public function id(): int|string|null - { - Log::debug(sprintf('Now at %s', __METHOD__)); - - return $this->user?->id; - } - - public function setUser(Authenticatable|User|null $user): void // @phpstan-ignore-line - { - Log::debug(sprintf('Now at %s', __METHOD__)); - if ($user instanceof User) { - $this->user = $user; - - return; - } - Log::error(sprintf('Did not set user at %s', __METHOD__)); - } - /** * @throws FireflyException * diff --git a/app/Support/Balance.php b/app/Support/Balance.php index 6b97a04628..f9d684c5ef 100644 --- a/app/Support/Balance.php +++ b/app/Support/Balance.php @@ -48,19 +48,18 @@ class Balance return $cache->get(); } - $query = Transaction::whereIn('transactions.account_id', $accounts->pluck('id')->toArray()) - ->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->orderBy('transaction_journals.date', 'desc') - ->orderBy('transaction_journals.order', 'asc') - ->orderBy('transaction_journals.description', 'desc') - ->orderBy('transactions.amount', 'desc') - ->where('transaction_journals.date', '<=', $date) - ; + $query = Transaction::whereIn('transactions.account_id', $accounts->pluck('id')->toArray()) + ->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->orderBy('transaction_journals.date', 'desc') + ->orderBy('transaction_journals.order', 'asc') + ->orderBy('transaction_journals.description', 'desc') + ->orderBy('transactions.amount', 'desc') + ->where('transaction_journals.date', '<=', $date); - $result = $query->get(['transactions.account_id', 'transactions.transaction_currency_id', 'transactions.balance_after']); + $result = $query->get(['transactions.account_id', 'transactions.transaction_currency_id', 'transactions.balance_after']); foreach ($result as $entry) { - $accountId = (int) $entry->account_id; - $currencyId = (int) $entry->transaction_currency_id; + $accountId = (int)$entry->account_id; + $currencyId = (int)$entry->transaction_currency_id; $currencies[$currencyId] ??= Amount::getTransactionCurrencyById($currencyId); $return[$accountId] ??= []; if (array_key_exists($currencyId, $return[$accountId])) { diff --git a/app/Support/Binder/AccountList.php b/app/Support/Binder/AccountList.php index 314a0c025f..3d6c48728e 100644 --- a/app/Support/Binder/AccountList.php +++ b/app/Support/Binder/AccountList.php @@ -43,23 +43,21 @@ class AccountList implements BinderInterface if ('allAssetAccounts' === $value) { /** @var Collection $collection */ $collection = auth()->user()->accounts() - ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') - ->whereIn('account_types.type', [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value]) - ->orderBy('accounts.name', 'ASC') - ->get(['accounts.*']) - ; + ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') + ->whereIn('account_types.type', [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value]) + ->orderBy('accounts.name', 'ASC') + ->get(['accounts.*']); } if ('allAssetAccounts' !== $value) { - $incoming = array_map('\intval', explode(',', $value)); - $list = array_merge(array_unique($incoming), [0]); + $incoming = array_map('\intval', explode(',', $value)); + $list = array_merge(array_unique($incoming), [0]); /** @var Collection $collection */ $collection = auth()->user()->accounts() - ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') - ->whereIn('accounts.id', $list) - ->orderBy('accounts.name', 'ASC') - ->get(['accounts.*']) - ; + ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') + ->whereIn('accounts.id', $list) + ->orderBy('accounts.name', 'ASC') + ->get(['accounts.*']); } if ($collection->count() > 0) { diff --git a/app/Support/Binder/BudgetList.php b/app/Support/Binder/BudgetList.php index 6526ebd38a..917885a7d0 100644 --- a/app/Support/Binder/BudgetList.php +++ b/app/Support/Binder/BudgetList.php @@ -41,13 +41,12 @@ class BudgetList implements BinderInterface if (auth()->check()) { if ('allBudgets' === $value) { return auth()->user()->budgets()->where('active', true) - ->orderBy('order', 'ASC') - ->orderBy('name', 'ASC') - ->get() - ; + ->orderBy('order', 'ASC') + ->orderBy('name', 'ASC') + ->get(); } - $list = array_unique(array_map('\intval', explode(',', $value))); + $list = array_unique(array_map('\intval', explode(',', $value))); if (0 === count($list)) { // @phpstan-ignore-line app('log')->warning('Budget list count is zero, return 404.'); @@ -57,10 +56,9 @@ class BudgetList implements BinderInterface /** @var Collection $collection */ $collection = auth()->user()->budgets() - ->where('active', true) - ->whereIn('id', $list) - ->get() - ; + ->where('active', true) + ->whereIn('id', $list) + ->get(); // add empty budget if applicable. if (in_array(0, $list, true)) { diff --git a/app/Support/Binder/CategoryList.php b/app/Support/Binder/CategoryList.php index 1275481fa3..cde58f228f 100644 --- a/app/Support/Binder/CategoryList.php +++ b/app/Support/Binder/CategoryList.php @@ -41,21 +41,19 @@ class CategoryList implements BinderInterface if (auth()->check()) { if ('allCategories' === $value) { return auth()->user()->categories() - ->orderBy('name', 'ASC') - ->get() - ; + ->orderBy('name', 'ASC') + ->get(); } - $list = array_unique(array_map('\intval', explode(',', $value))); + $list = array_unique(array_map('\intval', explode(',', $value))); if (0 === count($list)) { // @phpstan-ignore-line throw new NotFoundHttpException(); } /** @var Collection $collection */ $collection = auth()->user()->categories() - ->whereIn('id', $list) - ->get() - ; + ->whereIn('id', $list) + ->get(); // add empty category if applicable. if (in_array(0, $list, true)) { diff --git a/app/Support/Binder/Date.php b/app/Support/Binder/Date.php index 99c0ce4c17..4dcfb314c8 100644 --- a/app/Support/Binder/Date.php +++ b/app/Support/Binder/Date.php @@ -43,16 +43,16 @@ class Date implements BinderInterface /** @var FiscalHelperInterface $fiscalHelper */ $fiscalHelper = app(FiscalHelperInterface::class); - $magicWords = [ - 'currentMonthStart' => today(config('app.timezone'))->startOfMonth(), - 'currentMonthEnd' => today(config('app.timezone'))->endOfMonth(), - 'currentYearStart' => today(config('app.timezone'))->startOfYear(), - 'currentYearEnd' => today(config('app.timezone'))->endOfYear(), + $magicWords = [ + 'currentMonthStart' => today(config('app.timezone'))->startOfMonth(), + 'currentMonthEnd' => today(config('app.timezone'))->endOfMonth(), + 'currentYearStart' => today(config('app.timezone'))->startOfYear(), + 'currentYearEnd' => today(config('app.timezone'))->endOfYear(), - 'previousMonthStart' => today(config('app.timezone'))->startOfMonth()->subDay()->startOfMonth(), - 'previousMonthEnd' => today(config('app.timezone'))->startOfMonth()->subDay()->endOfMonth(), - 'previousYearStart' => today(config('app.timezone'))->startOfYear()->subDay()->startOfYear(), - 'previousYearEnd' => today(config('app.timezone'))->startOfYear()->subDay()->endOfYear(), + 'previousMonthStart' => today(config('app.timezone'))->startOfMonth()->subDay()->startOfMonth(), + 'previousMonthEnd' => today(config('app.timezone'))->startOfMonth()->subDay()->endOfMonth(), + 'previousYearStart' => today(config('app.timezone'))->startOfYear()->subDay()->startOfYear(), + 'previousYearEnd' => today(config('app.timezone'))->startOfYear()->subDay()->endOfYear(), 'currentFiscalYearStart' => $fiscalHelper->startOfFiscalYear(today(config('app.timezone'))), 'currentFiscalYearEnd' => $fiscalHelper->endOfFiscalYear(today(config('app.timezone'))), @@ -68,7 +68,7 @@ class Date implements BinderInterface try { $result = new Carbon($value); - } catch (InvalidDateException|InvalidFormatException $e) { // @phpstan-ignore-line + } catch (InvalidDateException | InvalidFormatException $e) { // @phpstan-ignore-line $message = sprintf('Could not parse date "%s" for user #%d: %s', $value, auth()->user()->id, $e->getMessage()); app('log')->error($message); diff --git a/app/Support/Binder/JournalList.php b/app/Support/Binder/JournalList.php index 5eadcc587a..217dd565ed 100644 --- a/app/Support/Binder/JournalList.php +++ b/app/Support/Binder/JournalList.php @@ -39,7 +39,7 @@ class JournalList implements BinderInterface public static function routeBinder(string $value, Route $route): array { if (auth()->check()) { - $list = self::parseList($value); + $list = self::parseList($value); // get the journals by using the collector. /** @var GroupCollectorInterface $collector */ @@ -47,7 +47,7 @@ class JournalList implements BinderInterface $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::TRANSFER->value, TransactionTypeEnum::RECONCILIATION->value]); $collector->withCategoryInformation()->withBudgetInformation()->withTagInformation()->withAccountInformation(); $collector->setJournalIds($list); - $result = $collector->getExtractedJournals(); + $result = $collector->getExtractedJournals(); if (0 === count($result)) { throw new NotFoundHttpException(); } diff --git a/app/Support/Binder/TagList.php b/app/Support/Binder/TagList.php index 3dd4835f54..685087da75 100644 --- a/app/Support/Binder/TagList.php +++ b/app/Support/Binder/TagList.php @@ -43,11 +43,10 @@ class TagList implements BinderInterface if (auth()->check()) { if ('allTags' === $value) { return auth()->user()->tags() - ->orderBy('tag', 'ASC') - ->get() - ; + ->orderBy('tag', 'ASC') + ->get(); } - $list = array_unique(array_map('\strtolower', explode(',', $value))); + $list = array_unique(array_map('\strtolower', explode(',', $value))); app('log')->debug('List of tags is', $list); if (0 === count($list)) { // @phpstan-ignore-line @@ -59,7 +58,7 @@ class TagList implements BinderInterface /** @var TagRepositoryInterface $repository */ $repository = app(TagRepositoryInterface::class); $repository->setUser(auth()->user()); - $allTags = $repository->get(); + $allTags = $repository->get(); $collection = $allTags->filter( static function (Tag $tag) use ($list) { @@ -68,7 +67,7 @@ class TagList implements BinderInterface return true; } - if (in_array((string) $tag->id, $list, true)) { + if (in_array((string)$tag->id, $list, true)) { Log::debug(sprintf('TagList: (id) found tag #%d ("%s") in list.', $tag->id, $tag->tag)); return true; diff --git a/app/Support/Binder/TagOrId.php b/app/Support/Binder/TagOrId.php index bc511e5018..ad3a866e1a 100644 --- a/app/Support/Binder/TagOrId.php +++ b/app/Support/Binder/TagOrId.php @@ -40,9 +40,9 @@ class TagOrId implements BinderInterface $repository = app(TagRepositoryInterface::class); $repository->setUser(auth()->user()); - $result = $repository->findByTag($value); + $result = $repository->findByTag($value); if (null === $result) { - $result = $repository->find((int) $value); + $result = $repository->find((int)$value); } if (null !== $result) { return $result; diff --git a/app/Support/Binder/UserGroupAccount.php b/app/Support/Binder/UserGroupAccount.php index 12d7eff4a2..47a7af5541 100644 --- a/app/Support/Binder/UserGroupAccount.php +++ b/app/Support/Binder/UserGroupAccount.php @@ -41,10 +41,9 @@ class UserGroupAccount implements BinderInterface if (auth()->check()) { /** @var User $user */ $user = auth()->user(); - $account = Account::where('id', (int) $value) - ->where('user_group_id', $user->user_group_id) - ->first() - ; + $account = Account::where('id', (int)$value) + ->where('user_group_id', $user->user_group_id) + ->first(); if (null !== $account) { return $account; } diff --git a/app/Support/Binder/UserGroupBill.php b/app/Support/Binder/UserGroupBill.php index 551846d693..05eff73b6e 100644 --- a/app/Support/Binder/UserGroupBill.php +++ b/app/Support/Binder/UserGroupBill.php @@ -41,10 +41,9 @@ class UserGroupBill implements BinderInterface if (auth()->check()) { /** @var User $user */ $user = auth()->user(); - $currency = Bill::where('id', (int) $value) - ->where('user_group_id', $user->user_group_id) - ->first() - ; + $currency = Bill::where('id', (int)$value) + ->where('user_group_id', $user->user_group_id) + ->first(); if (null !== $currency) { return $currency; } diff --git a/app/Support/Binder/UserGroupExchangeRate.php b/app/Support/Binder/UserGroupExchangeRate.php index 74a65c9348..1bb8fcc374 100644 --- a/app/Support/Binder/UserGroupExchangeRate.php +++ b/app/Support/Binder/UserGroupExchangeRate.php @@ -38,10 +38,9 @@ class UserGroupExchangeRate implements BinderInterface if (auth()->check()) { /** @var User $user */ $user = auth()->user(); - $rate = CurrencyExchangeRate::where('id', (int) $value) - ->where('user_group_id', $user->user_group_id) - ->first() - ; + $rate = CurrencyExchangeRate::where('id', (int)$value) + ->where('user_group_id', $user->user_group_id) + ->first(); if (null !== $rate) { return $rate; } diff --git a/app/Support/Binder/UserGroupTransaction.php b/app/Support/Binder/UserGroupTransaction.php index d9131400f3..61add59c73 100644 --- a/app/Support/Binder/UserGroupTransaction.php +++ b/app/Support/Binder/UserGroupTransaction.php @@ -38,10 +38,9 @@ class UserGroupTransaction implements BinderInterface if (auth()->check()) { /** @var User $user */ $user = auth()->user(); - $group = TransactionGroup::where('id', (int) $value) - ->where('user_group_id', $user->user_group_id) - ->first() - ; + $group = TransactionGroup::where('id', (int)$value) + ->where('user_group_id', $user->user_group_id) + ->first(); if (null !== $group) { return $group; } diff --git a/app/Support/CacheProperties.php b/app/Support/CacheProperties.php index b81f040467..38f2863e92 100644 --- a/app/Support/CacheProperties.php +++ b/app/Support/CacheProperties.php @@ -27,7 +27,6 @@ use Carbon\Carbon; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Cache; use JsonException; - use function Safe\json_encode; /** @@ -78,20 +77,6 @@ class CacheProperties return Cache::has($this->hash); } - private function hash(): void - { - $content = ''; - foreach ($this->properties as $property) { - try { - $content = sprintf('%s%s', $content, json_encode($property, JSON_THROW_ON_ERROR)); - } catch (JsonException) { - // @ignoreException - $content = sprintf('%s%s', $content, hash('sha256', (string) Carbon::now()->getTimestamp())); - } - } - $this->hash = substr(hash('sha256', $content), 0, 16); - } - /** * @param mixed $data */ @@ -99,4 +84,18 @@ class CacheProperties { Cache::forever($this->hash, $data); } + + private function hash(): void + { + $content = ''; + foreach ($this->properties as $property) { + try { + $content = sprintf('%s%s', $content, json_encode($property, JSON_THROW_ON_ERROR)); + } catch (JsonException) { + // @ignoreException + $content = sprintf('%s%s', $content, hash('sha256', (string)Carbon::now()->getTimestamp())); + } + } + $this->hash = substr(hash('sha256', $content), 0, 16); + } } diff --git a/app/Support/Calendar/Calculator.php b/app/Support/Calendar/Calculator.php index 3dff9dff07..b6ee2ceebb 100644 --- a/app/Support/Calendar/Calculator.php +++ b/app/Support/Calendar/Calculator.php @@ -33,31 +33,10 @@ use SplObjectStorage; */ class Calculator { - public const int DEFAULT_INTERVAL = 1; + public const int DEFAULT_INTERVAL = 1; private static ?SplObjectStorage $intervalMap = null; // @phpstan-ignore-line private static array $intervals = []; - /** - * @throws IntervalException - */ - public function nextDateByInterval(Carbon $epoch, Periodicity $periodicity, int $skipInterval = 0): Carbon - { - if (!self::isAvailablePeriodicity($periodicity)) { - throw IntervalException::unavailable($periodicity, self::$intervals); - } - - /** @var Periodicity\Interval $periodicity */ - $periodicity = self::$intervalMap->offsetGet($periodicity); - $interval = $this->skipInterval($skipInterval); - - return $periodicity->nextDate($epoch->clone(), $interval); - } - - public function isAvailablePeriodicity(Periodicity $periodicity): bool - { - return self::containsInterval($periodicity); - } - private static function containsInterval(Periodicity $periodicity): bool { return self::loadIntervalMap()->contains($periodicity); @@ -78,6 +57,27 @@ class Calculator return self::$intervalMap; } + public function isAvailablePeriodicity(Periodicity $periodicity): bool + { + return self::containsInterval($periodicity); + } + + /** + * @throws IntervalException + */ + public function nextDateByInterval(Carbon $epoch, Periodicity $periodicity, int $skipInterval = 0): Carbon + { + if (!self::isAvailablePeriodicity($periodicity)) { + throw IntervalException::unavailable($periodicity, self::$intervals); + } + + /** @var Periodicity\Interval $periodicity */ + $periodicity = self::$intervalMap->offsetGet($periodicity); + $interval = $this->skipInterval($skipInterval); + + return $periodicity->nextDate($epoch->clone(), $interval); + } + private function skipInterval(int $skip): int { return self::DEFAULT_INTERVAL + $skip; diff --git a/app/Support/Chart/Budget/FrontpageChartGenerator.php b/app/Support/Chart/Budget/FrontpageChartGenerator.php index 9e351afc78..e48cc65e42 100644 --- a/app/Support/Chart/Budget/FrontpageChartGenerator.php +++ b/app/Support/Chart/Budget/FrontpageChartGenerator.php @@ -69,9 +69,9 @@ class FrontpageChartGenerator Log::debug('Now in generate for budget chart.'); $budgets = $this->budgetRepository->getActiveBudgets(); $data = [ - ['label' => (string) trans('firefly.spent_in_budget'), 'entries' => [], 'type' => 'bar'], - ['label' => (string) trans('firefly.left_to_spend'), 'entries' => [], 'type' => 'bar'], - ['label' => (string) trans('firefly.overspent'), 'entries' => [], 'type' => 'bar'], + ['label' => (string)trans('firefly.spent_in_budget'), 'entries' => [], 'type' => 'bar'], + ['label' => (string)trans('firefly.left_to_spend'), 'entries' => [], 'type' => 'bar'], + ['label' => (string)trans('firefly.overspent'), 'entries' => [], 'type' => 'bar'], ]; // loop al budgets: @@ -84,6 +84,64 @@ class FrontpageChartGenerator return $data; } + public function setEnd(Carbon $end): void + { + $this->end = $end; + } + + public function setStart(Carbon $start): void + { + $this->start = $start; + } + + /** + * A basic setter for the user. Also updates the repositories with the right user. + */ + public function setUser(User $user): void + { + $this->budgetRepository->setUser($user); + $this->blRepository->setUser($user); + $this->opsRepository->setUser($user); + + $locale = app('steam')->getLocale(); + $this->monthAndDayFormat = (string)trans('config.month_and_day_js', [], $locale); + } + + /** + * If a budget has budget limit, each limit is processed individually. + */ + private function budgetLimits(array $data, Budget $budget, Collection $limits): array + { + Log::debug('Start processing budget limits.'); + + /** @var BudgetLimit $limit */ + foreach ($limits as $limit) { + $data = $this->processLimit($data, $budget, $limit); + } + Log::debug('Done processing budget limits.'); + + return $data; + } + + /** + * When no limits are present, the expenses of the whole period are collected and grouped. + * This is grouped per currency. Because there is no limit set, "left to spend" and "overspent" are empty. + */ + private function noBudgetLimits(array $data, Budget $budget): array + { + $spent = $this->opsRepository->sumExpenses($this->start, $this->end, null, new Collection()->push($budget)); + + /** @var array $entry */ + foreach ($spent as $entry) { + $title = sprintf('%s (%s)', $budget->name, $entry['currency_name']); + $data[0]['entries'][$title] = bcmul((string)$entry['sum'], '-1'); // spent + $data[1]['entries'][$title] = 0; // left to spend + $data[2]['entries'][$title] = 0; // overspent + } + + return $data; + } + /** * For each budget, gets all budget limits for the current time range. * When no limits are present, the time range is used to collect information on money spent. @@ -108,41 +166,6 @@ class FrontpageChartGenerator return $result; } - /** - * When no limits are present, the expenses of the whole period are collected and grouped. - * This is grouped per currency. Because there is no limit set, "left to spend" and "overspent" are empty. - */ - private function noBudgetLimits(array $data, Budget $budget): array - { - $spent = $this->opsRepository->sumExpenses($this->start, $this->end, null, new Collection()->push($budget)); - - /** @var array $entry */ - foreach ($spent as $entry) { - $title = sprintf('%s (%s)', $budget->name, $entry['currency_name']); - $data[0]['entries'][$title] = bcmul((string) $entry['sum'], '-1'); // spent - $data[1]['entries'][$title] = 0; // left to spend - $data[2]['entries'][$title] = 0; // overspent - } - - return $data; - } - - /** - * If a budget has budget limit, each limit is processed individually. - */ - private function budgetLimits(array $data, Budget $budget, Collection $limits): array - { - Log::debug('Start processing budget limits.'); - - /** @var BudgetLimit $limit */ - foreach ($limits as $limit) { - $data = $this->processLimit($data, $budget, $limit); - } - Log::debug('Done processing budget limits.'); - - return $data; - } - /** * For each limit, the expenses from the time range of the limit are collected. Each row from the result is * processed individually. @@ -158,7 +181,7 @@ class FrontpageChartGenerator Log::debug(sprintf('Processing limit #%d with %s %s', $limit->id, $limit->transactionCurrency->code, $limit->amount)); } - $spent = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection()->push($budget), $currency); + $spent = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection()->push($budget), $currency); Log::debug(sprintf('Spent array has %d entries.', count($spent))); /** @var array $entry */ @@ -185,7 +208,7 @@ class FrontpageChartGenerator */ private function processRow(array $data, Budget $budget, BudgetLimit $limit, array $entry): array { - $title = sprintf('%s (%s)', $budget->name, $entry['currency_name']); + $title = sprintf('%s (%s)', $budget->name, $entry['currency_name']); Log::debug(sprintf('Title is "%s"', $title)); if ($limit->start_date->startOfDay()->ne($this->start->startOfDay()) || $limit->end_date->startOfDay()->ne($this->end->startOfDay())) { $title = sprintf( @@ -196,22 +219,22 @@ class FrontpageChartGenerator $limit->end_date->isoFormat($this->monthAndDayFormat) ); } - $usePrimary = $this->convertToPrimary && $this->default->id !== $limit->transaction_currency_id; - $amount = $limit->amount; + $usePrimary = $this->convertToPrimary && $this->default->id !== $limit->transaction_currency_id; + $amount = $limit->amount; Log::debug(sprintf('Amount is "%s".', $amount)); if ($usePrimary && $limit->transaction_currency_id !== $this->default->id) { $amount = $limit->native_amount; Log::debug(sprintf('Amount is now "%s".', $amount)); } $amount ??= '0'; - $sumSpent = bcmul((string) $entry['sum'], '-1'); // spent + $sumSpent = bcmul((string)$entry['sum'], '-1'); // spent $data[0]['entries'][$title] ??= '0'; $data[1]['entries'][$title] ??= '0'; $data[2]['entries'][$title] ??= '0'; - $data[0]['entries'][$title] = bcadd((string) $data[0]['entries'][$title], 1 === bccomp($sumSpent, $amount) ? $amount : $sumSpent); // spent - $data[1]['entries'][$title] = bcadd((string) $data[1]['entries'][$title], 1 === bccomp($amount, $sumSpent) ? bcadd((string) $entry['sum'], $amount) : '0'); // left to spent - $data[2]['entries'][$title] = bcadd((string) $data[2]['entries'][$title], 1 === bccomp($amount, $sumSpent) ? '0' : bcmul(bcadd((string) $entry['sum'], $amount), '-1')); // overspent + $data[0]['entries'][$title] = bcadd((string)$data[0]['entries'][$title], 1 === bccomp($sumSpent, $amount) ? $amount : $sumSpent); // spent + $data[1]['entries'][$title] = bcadd((string)$data[1]['entries'][$title], 1 === bccomp($amount, $sumSpent) ? bcadd((string)$entry['sum'], $amount) : '0'); // left to spent + $data[2]['entries'][$title] = bcadd((string)$data[2]['entries'][$title], 1 === bccomp($amount, $sumSpent) ? '0' : bcmul(bcadd((string)$entry['sum'], $amount), '-1')); // overspent Log::debug(sprintf('Amount [spent] is now %s.', $data[0]['entries'][$title])); Log::debug(sprintf('Amount [left] is now %s.', $data[1]['entries'][$title])); @@ -219,27 +242,4 @@ class FrontpageChartGenerator return $data; } - - public function setEnd(Carbon $end): void - { - $this->end = $end; - } - - public function setStart(Carbon $start): void - { - $this->start = $start; - } - - /** - * A basic setter for the user. Also updates the repositories with the right user. - */ - public function setUser(User $user): void - { - $this->budgetRepository->setUser($user); - $this->blRepository->setUser($user); - $this->opsRepository->setUser($user); - - $locale = app('steam')->getLocale(); - $this->monthAndDayFormat = (string) trans('config.month_and_day_js', [], $locale); - } } diff --git a/app/Support/Chart/Category/FrontpageChartGenerator.php b/app/Support/Chart/Category/FrontpageChartGenerator.php index c27f13c686..b106deafce 100644 --- a/app/Support/Chart/Category/FrontpageChartGenerator.php +++ b/app/Support/Chart/Category/FrontpageChartGenerator.php @@ -26,7 +26,6 @@ namespace FireflyIII\Support\Chart\Category; use Carbon\Carbon; use FireflyIII\Enums\AccountTypeEnum; -use FireflyIII\Models\Category; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; @@ -66,16 +65,16 @@ class FrontpageChartGenerator public function generate(): array { Log::debug(sprintf('Now in %s', __METHOD__)); - $categories = $this->repository->getCategories(); - $accounts = $this->accountRepos->getAccountsByType([AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value]); - $collection = $this->collectExpensesAll($categories, $accounts); + $categories = $this->repository->getCategories(); + $accounts = $this->accountRepos->getAccountsByType([AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value]); + $collection = $this->collectExpensesAll($categories, $accounts); // collect for no-category: - $noCategory = $this->collectNoCatExpenses($accounts); - $collection = array_merge($collection, $noCategory); + $noCategory = $this->collectNoCatExpenses($accounts); + $collection = array_merge($collection, $noCategory); // sort temp array by amount. - $amounts = array_column($collection, 'sum_float'); + $amounts = array_column($collection, 'sum_float'); array_multisort($amounts, SORT_ASC, $collection); $currencyData = $this->createCurrencyGroups($collection); @@ -96,6 +95,30 @@ class FrontpageChartGenerator ]; } + private function collectExpensesAll(Collection $categories, Collection $accounts): array + { + Log::debug(sprintf('Collect expenses for %d category(ies).', count($categories))); + $spent = $this->opsRepos->collectExpenses($this->start, $this->end, $accounts, $categories); + $tempData = []; + foreach ($categories as $category) { + $sums = $this->opsRepos->sumCollectedTransactionsByCategory($spent, $category, 'negative', $this->convertToPrimary); + if (0 === count($sums)) { + continue; + } + foreach ($sums as $currency) { + $this->addCurrency($currency); + $tempData[] = [ + 'name' => $category->name, + 'sum' => $currency['sum'], + 'sum_float' => round((float)$currency['sum'], $currency['currency_decimal_places']), + 'currency_id' => (int)$currency['currency_id'], + ]; + } + } + + return $tempData; + } + private function collectNoCatExpenses(Collection $accounts): array { $noCatExp = $this->noCatRepos->sumExpenses($this->start, $this->end, $accounts); @@ -147,28 +170,4 @@ class FrontpageChartGenerator return $currencyData; } - - private function collectExpensesAll(Collection $categories, Collection $accounts): array - { - Log::debug(sprintf('Collect expenses for %d category(ies).', count($categories))); - $spent = $this->opsRepos->collectExpenses($this->start, $this->end, $accounts, $categories); - $tempData = []; - foreach ($categories as $category) { - $sums = $this->opsRepos->sumCollectedTransactionsByCategory($spent, $category, 'negative', $this->convertToPrimary); - if (0 === count($sums)) { - continue; - } - foreach ($sums as $currency) { - $this->addCurrency($currency); - $tempData[] = [ - 'name' => $category->name, - 'sum' => $currency['sum'], - 'sum_float' => round((float)$currency['sum'], $currency['currency_decimal_places']), - 'currency_id' => (int)$currency['currency_id'], - ]; - } - } - - return $tempData; - } } diff --git a/app/Support/Chart/Category/WholePeriodChartGenerator.php b/app/Support/Chart/Category/WholePeriodChartGenerator.php index 2a28cb4d62..044b7f28a2 100644 --- a/app/Support/Chart/Category/WholePeriodChartGenerator.php +++ b/app/Support/Chart/Category/WholePeriodChartGenerator.php @@ -40,22 +40,22 @@ class WholePeriodChartGenerator public function generate(Category $category, Carbon $start, Carbon $end): array { - $collection = new Collection()->push($category); + $collection = new Collection()->push($category); /** @var OperationsRepositoryInterface $opsRepository */ - $opsRepository = app(OperationsRepositoryInterface::class); + $opsRepository = app(OperationsRepositoryInterface::class); /** @var AccountRepositoryInterface $accountRepository */ $accountRepository = app(AccountRepositoryInterface::class); - $types = [AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value]; - $accounts = $accountRepository->getAccountsByType($types); - $step = $this->calculateStep($start, $end); - $chartData = []; - $spent = []; - $earned = []; + $types = [AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value]; + $accounts = $accountRepository->getAccountsByType($types); + $step = $this->calculateStep($start, $end); + $chartData = []; + $spent = []; + $earned = []; - $current = clone $start; + $current = clone $start; while ($current <= $end) { $key = $current->format('Y-m-d'); @@ -65,33 +65,33 @@ class WholePeriodChartGenerator $current = app('navigation')->addPeriod($current, $step, 0); } - $currencies = $this->extractCurrencies($spent) + $this->extractCurrencies($earned); + $currencies = $this->extractCurrencies($spent) + $this->extractCurrencies($earned); // generate chart data (for each currency) /** @var array $currency */ foreach ($currencies as $currency) { - $code = $currency['currency_code']; - $name = $currency['currency_name']; - $chartData[sprintf('spent-in-%s', $code)] = [ - 'label' => (string) trans('firefly.box_spent_in_currency', ['currency' => $name]), + $code = $currency['currency_code']; + $name = $currency['currency_name']; + $chartData[sprintf('spent-in-%s', $code)] = [ + 'label' => (string)trans('firefly.box_spent_in_currency', ['currency' => $name]), 'entries' => [], 'type' => 'bar', 'backgroundColor' => 'rgba(219, 68, 55, 0.5)', // red ]; $chartData[sprintf('earned-in-%s', $code)] = [ - 'label' => (string) trans('firefly.box_earned_in_currency', ['currency' => $name]), + 'label' => (string)trans('firefly.box_earned_in_currency', ['currency' => $name]), 'entries' => [], 'type' => 'bar', 'backgroundColor' => 'rgba(0, 141, 76, 0.5)', // green ]; } - $current = clone $start; + $current = clone $start; while ($current <= $end) { - $key = $current->format('Y-m-d'); - $label = app('navigation')->periodShow($current, $step); + $key = $current->format('Y-m-d'); + $label = app('navigation')->periodShow($current, $step); /** @var array $currency */ foreach ($currencies as $currency) { diff --git a/app/Support/Chart/ChartData.php b/app/Support/Chart/ChartData.php index ab03e3dd31..8d58c6a304 100644 --- a/app/Support/Chart/ChartData.php +++ b/app/Support/Chart/ChartData.php @@ -44,12 +44,12 @@ class ChartData public function add(array $data): void { if (array_key_exists('currency_id', $data)) { - $data['currency_id'] = (string) $data['currency_id']; + $data['currency_id'] = (string)$data['currency_id']; } if (array_key_exists('primary_currency_id', $data)) { - $data['primary_currency_id'] = (string) $data['primary_currency_id']; + $data['primary_currency_id'] = (string)$data['primary_currency_id']; } - $required = ['start', 'date', 'end', 'entries']; + $required = ['start', 'date', 'end', 'entries']; foreach ($required as $field) { if (!array_key_exists($field, $data)) { throw new FireflyException(sprintf('Data-set is missing the "%s"-variable.', $field)); diff --git a/app/Support/ChartColour.php b/app/Support/ChartColour.php index 9e938b9946..f08de5c258 100644 --- a/app/Support/ChartColour.php +++ b/app/Support/ChartColour.php @@ -55,7 +55,7 @@ class ChartColour public static function getColour(int $index): string { $index %= count(self::$colours); - $row = self::$colours[$index]; + $row = self::$colours[$index]; return sprintf('rgba(%d, %d, %d, 0.7)', $row[0], $row[1], $row[2]); } diff --git a/app/Support/Cronjobs/AutoBudgetCronjob.php b/app/Support/Cronjobs/AutoBudgetCronjob.php index 9a2a5a0d66..6855884d66 100644 --- a/app/Support/Cronjobs/AutoBudgetCronjob.php +++ b/app/Support/Cronjobs/AutoBudgetCronjob.php @@ -39,7 +39,7 @@ class AutoBudgetCronjob extends AbstractCronjob { /** @var Configuration $config */ $config = FireflyConfig::get('last_ab_job', 0); - $lastTime = (int) $config->data; + $lastTime = (int)$config->data; $diff = now(config('app.timezone'))->getTimestamp() - $lastTime; $diffForHumans = now(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true); if (0 === $lastTime) { @@ -70,7 +70,7 @@ class AutoBudgetCronjob extends AbstractCronjob Log::info(sprintf('Will now fire auto budget cron job task for date "%s".', $this->date->format('Y-m-d'))); /** @var CreateAutoBudgetLimits $job */ - $job = app(CreateAutoBudgetLimits::class, [$this->date]); + $job = app(CreateAutoBudgetLimits::class, [$this->date]); $job->setDate($this->date); $job->handle(); @@ -80,7 +80,7 @@ class AutoBudgetCronjob extends AbstractCronjob $this->jobSucceeded = true; $this->message = 'Auto-budget cron job fired successfully.'; - FireflyConfig::set('last_ab_job', (int) $this->date->format('U')); + FireflyConfig::set('last_ab_job', (int)$this->date->format('U')); Log::info('Done with auto budget cron job task.'); } } diff --git a/app/Support/Cronjobs/BillWarningCronjob.php b/app/Support/Cronjobs/BillWarningCronjob.php index 8a30bb9c0a..f192aa1224 100644 --- a/app/Support/Cronjobs/BillWarningCronjob.php +++ b/app/Support/Cronjobs/BillWarningCronjob.php @@ -45,7 +45,7 @@ class BillWarningCronjob extends AbstractCronjob /** @var Configuration $config */ $config = FireflyConfig::get('last_bw_job', 0); - $lastTime = (int) $config->data; + $lastTime = (int)$config->data; $diff = now(config('app.timezone'))->getTimestamp() - $lastTime; $diffForHumans = now(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true); @@ -82,7 +82,7 @@ class BillWarningCronjob extends AbstractCronjob Log::info(sprintf('Will now fire bill notification job task for date "%s".', $this->date->format('Y-m-d H:i:s'))); /** @var WarnAboutBills $job */ - $job = app(WarnAboutBills::class); + $job = app(WarnAboutBills::class); $job->setDate($this->date); $job->setForce($this->force); $job->handle(); @@ -93,8 +93,8 @@ class BillWarningCronjob extends AbstractCronjob $this->jobSucceeded = true; $this->message = 'Bill notification cron job fired successfully.'; - FireflyConfig::set('last_bw_job', (int) $this->date->format('U')); - Log::info(sprintf('Marked the last time this job has run as "%s" (%d)', $this->date->format('Y-m-d H:i:s'), (int) $this->date->format('U'))); + FireflyConfig::set('last_bw_job', (int)$this->date->format('U')); + Log::info(sprintf('Marked the last time this job has run as "%s" (%d)', $this->date->format('Y-m-d H:i:s'), (int)$this->date->format('U'))); Log::info('Done with bill notification cron job task.'); } } diff --git a/app/Support/Cronjobs/ExchangeRatesCronjob.php b/app/Support/Cronjobs/ExchangeRatesCronjob.php index 71c9a8e587..57cb788bc7 100644 --- a/app/Support/Cronjobs/ExchangeRatesCronjob.php +++ b/app/Support/Cronjobs/ExchangeRatesCronjob.php @@ -39,7 +39,7 @@ class ExchangeRatesCronjob extends AbstractCronjob { /** @var Configuration $config */ $config = FireflyConfig::get('last_cer_job', 0); - $lastTime = (int) $config->data; + $lastTime = (int)$config->data; $diff = now(config('app.timezone'))->getTimestamp() - $lastTime; $diffForHumans = now(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true); if (0 === $lastTime) { @@ -71,7 +71,7 @@ class ExchangeRatesCronjob extends AbstractCronjob Log::info(sprintf('Will now fire exchange rates cron job task for date "%s".', $this->date->format('Y-m-d'))); /** @var DownloadExchangeRates $job */ - $job = app(DownloadExchangeRates::class); + $job = app(DownloadExchangeRates::class); $job->setDate($this->date); $job->handle(); @@ -81,7 +81,7 @@ class ExchangeRatesCronjob extends AbstractCronjob $this->jobSucceeded = true; $this->message = 'Exchange rates cron job fired successfully.'; - FireflyConfig::set('last_cer_job', (int) $this->date->format('U')); + FireflyConfig::set('last_cer_job', (int)$this->date->format('U')); Log::info('Done with exchange rates job task.'); } } diff --git a/app/Support/Cronjobs/RecurringCronjob.php b/app/Support/Cronjobs/RecurringCronjob.php index c722733253..1f8654b9a7 100644 --- a/app/Support/Cronjobs/RecurringCronjob.php +++ b/app/Support/Cronjobs/RecurringCronjob.php @@ -45,7 +45,7 @@ class RecurringCronjob extends AbstractCronjob /** @var Configuration $config */ $config = FireflyConfig::get('last_rt_job', 0); - $lastTime = (int) $config->data; + $lastTime = (int)$config->data; $diff = now(config('app.timezone'))->getTimestamp() - $lastTime; $diffForHumans = now(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true); @@ -80,7 +80,7 @@ class RecurringCronjob extends AbstractCronjob { Log::info(sprintf('Will now fire recurring cron job task for date "%s".', $this->date->format('Y-m-d H:i:s'))); - $job = new CreateRecurringTransactions($this->date); + $job = new CreateRecurringTransactions($this->date); $job->setForce($this->force); $job->handle(); @@ -90,8 +90,8 @@ class RecurringCronjob extends AbstractCronjob $this->jobSucceeded = true; $this->message = 'Recurring transactions cron job fired successfully.'; - FireflyConfig::set('last_rt_job', (int) $this->date->format('U')); - Log::info(sprintf('Marked the last time this job has run as "%s" (%d)', $this->date->format('Y-m-d H:i:s'), (int) $this->date->format('U'))); + FireflyConfig::set('last_rt_job', (int)$this->date->format('U')); + Log::info(sprintf('Marked the last time this job has run as "%s" (%d)', $this->date->format('Y-m-d H:i:s'), (int)$this->date->format('U'))); Log::info('Done with recurring cron job task.'); } } diff --git a/app/Support/Cronjobs/UpdateCheckCronjob.php b/app/Support/Cronjobs/UpdateCheckCronjob.php index 6d3cea13ab..c7681037dd 100644 --- a/app/Support/Cronjobs/UpdateCheckCronjob.php +++ b/app/Support/Cronjobs/UpdateCheckCronjob.php @@ -41,8 +41,8 @@ class UpdateCheckCronjob extends AbstractCronjob Log::debug('Now in checkForUpdates()'); // should not check for updates: - $permission = FireflyConfig::get('permission_update_check', -1); - $value = (int) $permission->data; + $permission = FireflyConfig::get('permission_update_check', -1); + $value = (int)$permission->data; if (1 !== $value) { Log::debug('Update check is not enabled.'); // get stuff from job: @@ -56,9 +56,9 @@ class UpdateCheckCronjob extends AbstractCronjob // TODO this is duplicate. /** @var Configuration $lastCheckTime */ - $lastCheckTime = FireflyConfig::get('last_update_check', Carbon::now()->getTimestamp()); - $now = Carbon::now()->getTimestamp(); - $diff = $now - $lastCheckTime->data; + $lastCheckTime = FireflyConfig::get('last_update_check', Carbon::now()->getTimestamp()); + $now = Carbon::now()->getTimestamp(); + $diff = $now - $lastCheckTime->data; Log::debug(sprintf('Last check time is %d, current time is %d, difference is %d', $lastCheckTime->data, $now, $diff)); if ($diff < 604800 && false === $this->force) { // get stuff from job: @@ -71,7 +71,7 @@ class UpdateCheckCronjob extends AbstractCronjob } // last check time was more than a week ago. Log::debug('Have not checked for a new version in a week!'); - $release = $this->getLatestRelease(); + $release = $this->getLatestRelease(); if ('error' === $release['level']) { // get stuff from job: $this->jobFired = true; diff --git a/app/Support/Cronjobs/WebhookCronjob.php b/app/Support/Cronjobs/WebhookCronjob.php index 0cd1899380..84ea6676f5 100644 --- a/app/Support/Cronjobs/WebhookCronjob.php +++ b/app/Support/Cronjobs/WebhookCronjob.php @@ -45,7 +45,7 @@ class WebhookCronjob extends AbstractCronjob /** @var Configuration $config */ $config = FireflyConfig::get('last_webhook_job', 0); - $lastTime = (int) $config->data; + $lastTime = (int)$config->data; $diff = now(config('app.timezone'))->getTimestamp() - $lastTime; $diffForHumans = now(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true); @@ -90,8 +90,8 @@ class WebhookCronjob extends AbstractCronjob $this->jobSucceeded = true; $this->message = 'Send webhook messages cron job fired successfully.'; - FireflyConfig::set('last_webhook_job', (int) $this->date->format('U')); - Log::info(sprintf('Marked the last time this job has run as "%s" (%d)', $this->date->format('Y-m-d H:i:s'), (int) $this->date->format('U'))); + FireflyConfig::set('last_webhook_job', (int)$this->date->format('U')); + Log::info(sprintf('Marked the last time this job has run as "%s" (%d)', $this->date->format('Y-m-d H:i:s'), (int)$this->date->format('U'))); Log::info('Done with webhook cron job task.'); } } diff --git a/app/Support/Debug/Timer.php b/app/Support/Debug/Timer.php index 94e48187b7..b23e941a0c 100644 --- a/app/Support/Debug/Timer.php +++ b/app/Support/Debug/Timer.php @@ -28,8 +28,8 @@ use Illuminate\Support\Facades\Log; class Timer { - private array $times = []; private static ?Timer $instance = null; + private array $times = []; private function __construct() { diff --git a/app/Support/ExpandedForm.php b/app/Support/ExpandedForm.php index e460353ac2..1dbaeb7d8e 100644 --- a/app/Support/ExpandedForm.php +++ b/app/Support/ExpandedForm.php @@ -23,9 +23,9 @@ declare(strict_types=1); namespace FireflyIII\Support; -use Illuminate\Database\Eloquent\Model; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Support\Form\FormSupport; +use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; use Throwable; @@ -43,7 +43,7 @@ class ExpandedForm */ public function amountNoCurrency(string $name, $value = null, ?array $options = null): string { - $options ??= []; + $options ??= []; $label = $this->label($name, $options); $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); @@ -74,8 +74,8 @@ class ExpandedForm */ public function checkbox(string $name, ?int $value = null, $checked = null, ?array $options = null): string { - $options ??= []; - $value ??= 1; + $options ??= []; + $value ??= 1; $options['checked'] = true === $checked; if (app('session')->has('preFilled')) { @@ -83,10 +83,10 @@ class ExpandedForm $options['checked'] = $preFilled[$name] ?? $options['checked']; } - $label = $this->label($name, $options); - $options = $this->expandOptionArray($name, $label, $options); - $classes = $this->getHolderClasses($name); - $value = $this->fillFieldValue($name, $value); + $label = $this->label($name, $options); + $options = $this->expandOptionArray($name, $label, $options); + $classes = $this->getHolderClasses($name); + $value = $this->fillFieldValue($name, $value); unset($options['placeholder'], $options['autocomplete'], $options['class']); @@ -157,10 +157,10 @@ class ExpandedForm public function integer(string $name, $value = null, ?array $options = null): string { $options ??= []; - $label = $this->label($name, $options); - $options = $this->expandOptionArray($name, $label, $options); - $classes = $this->getHolderClasses($name); - $value = $this->fillFieldValue($name, $value); + $label = $this->label($name, $options); + $options = $this->expandOptionArray($name, $label, $options); + $classes = $this->getHolderClasses($name); + $value = $this->fillFieldValue($name, $value); $options['step'] ??= '1'; try { @@ -209,9 +209,9 @@ class ExpandedForm /** @var Model $entry */ foreach ($set as $entry) { // All Eloquent models have an ID - $entryId = $entry->id; - $current = $entry->toArray(); - $title = null; + $entryId = $entry->id; + $current = $entry->toArray(); + $title = null; foreach ($fields as $field) { if (array_key_exists($field, $current) && null === $title) { $title = $current[$field]; diff --git a/app/Support/Export/ExportDataGenerator.php b/app/Support/Export/ExportDataGenerator.php index d4a44e4e6a..b70f2a6615 100644 --- a/app/Support/Export/ExportDataGenerator.php +++ b/app/Support/Export/ExportDataGenerator.php @@ -85,12 +85,12 @@ class ExportDataGenerator private bool $exportTransactions; private Carbon $start; private User $user; - private UserGroup $userGroup; // @phpstan-ignore-line + private UserGroup $userGroup; // @phpstan-ignore-line public function __construct() { - $this->accounts = new Collection(); - $this->start = today(config('app.timezone')); + $this->accounts = new Collection(); + $this->start = today(config('app.timezone')); $this->start->subYear(); $this->end = today(config('app.timezone')); $this->exportTransactions = false; @@ -141,453 +141,6 @@ class ExportDataGenerator return $return; } - /** - * @throws CannotInsertRecord - * @throws Exception - * @throws FireflyException - */ - private function exportAccounts(): string - { - $header = [ - 'user_id', - 'account_id', - 'created_at', - 'updated_at', - 'type', - 'name', - 'virtual_balance', - 'iban', - 'number', - 'active', - 'currency_code', - 'role', - 'cc_type', - 'cc_payment_date', - 'in_net_worth', - 'interest', - 'interest_period', - ]; - - /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class); - $repository->setUser($this->user); - $allAccounts = $repository->getAccountsByType([]); - $records = []; - - /** @var Account $account */ - foreach ($allAccounts as $account) { - $currency = $repository->getAccountCurrency($account); - $records[] = [ - $this->user->id, - $account->id, - $account->created_at->toAtomString(), - $account->updated_at->toAtomString(), - $account->accountType->type, - $account->name, - $account->virtual_balance, - $account->iban, - $account->account_number, - $account->active, - $currency?->code, - $repository->getMetaValue($account, 'account_role'), - $repository->getMetaValue($account, 'cc_type'), - $repository->getMetaValue($account, 'cc_monthly_payment_date'), - $repository->getMetaValue($account, 'include_net_worth'), - $repository->getMetaValue($account, 'interest'), - $repository->getMetaValue($account, 'interest_period'), - ]; - } - - // load the CSV document from a string - $csv = Writer::createFromString(); - - // insert the header - try { - $csv->insertOne($header); - } catch (CannotInsertRecord $e) { - throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); - } - - // insert all the records - $csv->insertAll($records); - - try { - $string = $csv->toString(); - } catch (Exception $e) { // intentional generic exception - app('log')->error($e->getMessage()); - - throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); - } - - return $string; - } - - public function setUser(User $user): void - { - $this->user = $user; - } - - /** - * @throws CannotInsertRecord - * @throws Exception - * @throws FireflyException - */ - private function exportBills(): string - { - /** @var BillRepositoryInterface $repository */ - $repository = app(BillRepositoryInterface::class); - $repository->setUser($this->user); - $bills = $repository->getBills(); - $header = [ - 'user_id', - 'bill_id', - 'created_at', - 'updated_at', - 'currency_code', - 'name', - 'amount_min', - 'amount_max', - 'date', - 'repeat_freq', - 'skip', - 'active', - ]; - $records = []; - - /** @var Bill $bill */ - foreach ($bills as $bill) { - $records[] = [ - $this->user->id, - $bill->id, - $bill->created_at->toAtomString(), - $bill->updated_at->toAtomString(), - $bill->transactionCurrency->code, - $bill->name, - $bill->amount_min, - $bill->amount_max, - $bill->date->format('Y-m-d'), - $bill->repeat_freq, - $bill->skip, - $bill->active, - ]; - } - - // load the CSV document from a string - $csv = Writer::createFromString(); - - // insert the header - try { - $csv->insertOne($header); - } catch (CannotInsertRecord $e) { - throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); - } - - // insert all the records - $csv->insertAll($records); - - try { - $string = $csv->toString(); - } catch (Exception $e) { // intentional generic exception - app('log')->error($e->getMessage()); - - throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); - } - - return $string; - } - - /** - * @throws CannotInsertRecord - * @throws Exception - * @throws FireflyException - */ - private function exportBudgets(): string - { - $header = [ - 'user_id', - 'budget_id', - 'name', - 'active', - 'order', - 'start_date', - 'end_date', - 'currency_code', - 'amount', - ]; - - $budgetRepos = app(BudgetRepositoryInterface::class); - $budgetRepos->setUser($this->user); - $limitRepos = app(BudgetLimitRepositoryInterface::class); - $budgets = $budgetRepos->getBudgets(); - $records = []; - - /** @var Budget $budget */ - foreach ($budgets as $budget) { - $limits = $limitRepos->getBudgetLimits($budget); - - /** @var BudgetLimit $limit */ - foreach ($limits as $limit) { - $records[] = [ - $this->user->id, - $budget->id, - $budget->name, - $budget->active, - $budget->order, - $limit->start_date->format('Y-m-d'), - $limit->end_date->format('Y-m-d'), - $limit->transactionCurrency->code, - $limit->amount, - ]; - } - } - - // load the CSV document from a string - $csv = Writer::createFromString(); - - // insert the header - try { - $csv->insertOne($header); - } catch (CannotInsertRecord $e) { - throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); - } - - // insert all the records - $csv->insertAll($records); - - try { - $string = $csv->toString(); - } catch (Exception $e) { // intentional generic exception - app('log')->error($e->getMessage()); - - throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); - } - - return $string; - } - - /** - * @throws CannotInsertRecord - * @throws Exception - * @throws FireflyException - */ - private function exportCategories(): string - { - $header = ['user_id', 'category_id', 'created_at', 'updated_at', 'name']; - - /** @var CategoryRepositoryInterface $catRepos */ - $catRepos = app(CategoryRepositoryInterface::class); - $catRepos->setUser($this->user); - - $records = []; - $categories = $catRepos->getCategories(); - - /** @var Category $category */ - foreach ($categories as $category) { - $records[] = [ - $this->user->id, - $category->id, - $category->created_at->toAtomString(), - $category->updated_at->toAtomString(), - $category->name, - ]; - } - - // load the CSV document from a string - $csv = Writer::createFromString(); - - // insert the header - try { - $csv->insertOne($header); - } catch (CannotInsertRecord $e) { - throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); - } - - // insert all the records - $csv->insertAll($records); - - try { - $string = $csv->toString(); - } catch (Exception $e) { // intentional generic exception - app('log')->error($e->getMessage()); - - throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); - } - - return $string; - } - - /** - * @throws CannotInsertRecord - * @throws Exception - * @throws FireflyException - */ - private function exportPiggies(): string - { - /** @var PiggyBankRepositoryInterface $piggyRepos */ - $piggyRepos = app(PiggyBankRepositoryInterface::class); - $piggyRepos->setUser($this->user); - - /** @var AccountRepositoryInterface $accountRepos */ - $accountRepos = app(AccountRepositoryInterface::class); - $accountRepos->setUser($this->user); - - $header = [ - 'user_id', - 'piggy_bank_id', - 'created_at', - 'updated_at', - 'account_name', - 'account_type', - 'name', - 'currency_code', - 'target_amount', - 'current_amount', - 'start_date', - 'target_date', - 'order', - 'active', - ]; - $records = []; - $piggies = $piggyRepos->getPiggyBanks(); - - /** @var PiggyBank $piggy */ - foreach ($piggies as $piggy) { - $repetition = $piggyRepos->getRepetition($piggy); - $currency = $accountRepos->getAccountCurrency($piggy->account); - $records[] = [ - $this->user->id, - $piggy->id, - $piggy->created_at->toAtomString(), - $piggy->updated_at->toAtomString(), - $piggy->account->name, - $piggy->account->accountType->type, - $piggy->name, - $currency?->code, - $piggy->target_amount, - $repetition?->current_amount, - $piggy->start_date?->format('Y-m-d'), - $piggy->target_date?->format('Y-m-d'), - $piggy->order, - $piggy->active, - ]; - } - - // load the CSV document from a string - $csv = Writer::createFromString(); - - // insert the header - try { - $csv->insertOne($header); - } catch (CannotInsertRecord $e) { - throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); - } - - // insert all the records - $csv->insertAll($records); - - try { - $string = $csv->toString(); - } catch (Exception $e) { // intentional generic exception - app('log')->error($e->getMessage()); - - throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); - } - - return $string; - } - - /** - * @throws CannotInsertRecord - * @throws Exception - * @throws FireflyException - */ - private function exportRecurring(): string - { - /** @var RecurringRepositoryInterface $recurringRepos */ - $recurringRepos = app(RecurringRepositoryInterface::class); - $recurringRepos->setUser($this->user); - $header = [ - // recurrence: - 'user_id', 'recurrence_id', 'row_contains', 'created_at', 'updated_at', 'type', 'title', 'description', 'first_date', 'repeat_until', 'latest_date', 'repetitions', 'apply_rules', 'active', - - // repetition info: - 'type', 'moment', 'skip', 'weekend', - // transactions + meta: - 'currency_code', 'foreign_currency_code', 'source_name', 'source_type', 'destination_name', 'destination_type', 'amount', 'foreign_amount', 'category', 'budget', 'piggy_bank', 'tags', - ]; - $records = []; - $recurrences = $recurringRepos->get(); - - /** @var Recurrence $recurrence */ - foreach ($recurrences as $recurrence) { - // add recurrence: - $records[] = [ - $this->user->id, $recurrence->id, - 'recurrence', - $recurrence->created_at->toAtomString(), $recurrence->updated_at->toAtomString(), $recurrence->transactionType->type, $recurrence->title, $recurrence->description, $recurrence->first_date?->format('Y-m-d'), $recurrence->repeat_until?->format('Y-m-d'), $recurrence->latest_date?->format('Y-m-d'), $recurrence->repetitions, $recurrence->apply_rules, $recurrence->active, - ]; - - // add new row for each repetition - /** @var RecurrenceRepetition $repetition */ - foreach ($recurrence->recurrenceRepetitions as $repetition) { - $records[] = [ - // recurrence - $this->user->id, - $recurrence->id, - 'repetition', - null, null, null, null, null, null, null, null, null, null, null, - - // repetition: - $repetition->repetition_type, $repetition->repetition_moment, $repetition->repetition_skip, $repetition->weekend, - ]; - } - - /** @var RecurrenceTransaction $transaction */ - foreach ($recurrence->recurrenceTransactions as $transaction) { - $categoryName = $recurringRepos->getCategoryName($transaction); - $budgetId = $recurringRepos->getBudget($transaction); - $piggyBankId = $recurringRepos->getPiggyBank($transaction); - $tags = $recurringRepos->getTags($transaction); - - $records[] = [ - // recurrence - $this->user->id, - $recurrence->id, - 'transaction', - null, null, null, null, null, null, null, null, null, null, null, - - // repetition: - null, null, null, null, - - // transaction: - $transaction->transactionCurrency->code, $transaction->foreignCurrency?->code, $transaction->sourceAccount->name, $transaction->sourceAccount->accountType->type, $transaction->destinationAccount->name, $transaction->destinationAccount->accountType->type, $transaction->amount, $transaction->foreign_amount, $categoryName, $budgetId, $piggyBankId, implode(',', $tags), - ]; - } - } - // load the CSV document from a string - $csv = Writer::createFromString(); - - // insert the header - try { - $csv->insertOne($header); - } catch (CannotInsertRecord $e) { - throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); - } - - // insert all the records - $csv->insertAll($records); - - try { - $string = $csv->toString(); - } catch (Exception $e) { // intentional generic exception - app('log')->error($e->getMessage()); - - throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); - } - - return $string; - } - /** * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ @@ -596,256 +149,6 @@ class ExportDataGenerator return null; } - /** - * @throws CannotInsertRecord - * @throws Exception - * @throws FireflyException - */ - private function exportRules(): string - { - $header = [ - 'user_id', 'rule_id', 'row_contains', - 'created_at', 'updated_at', 'group_id', 'title', 'description', 'order', 'active', 'stop_processing', 'strict', - 'trigger_type', 'trigger_value', 'trigger_order', 'trigger_active', 'trigger_stop_processing', - 'action_type', 'action_value', 'action_order', 'action_active', 'action_stop_processing']; - $ruleRepos = app(RuleRepositoryInterface::class); - $ruleRepos->setUser($this->user); - $rules = $ruleRepos->getAll(); - $records = []; - - /** @var Rule $rule */ - foreach ($rules as $rule) { - $entry = [ - $this->user->id, $rule->id, - 'rule', - $rule->created_at->toAtomString(), $rule->updated_at->toAtomString(), $rule->ruleGroup->id, $rule->ruleGroup->title, $rule->title, $rule->description, $rule->order, $rule->active, $rule->stop_processing, $rule->strict, - null, null, null, null, null, null, null, null, null, - ]; - $records[] = $entry; - - /** @var RuleTrigger $trigger */ - foreach ($rule->ruleTriggers as $trigger) { - $entry = [ - $this->user->id, - $rule->id, - 'trigger', - null, null, null, null, null, null, null, null, null, - $trigger->trigger_type, $trigger->trigger_value, $trigger->order, $trigger->active, $trigger->stop_processing, - null, null, null, null, null, - ]; - $records[] = $entry; - } - - /** @var RuleAction $action */ - foreach ($rule->ruleActions as $action) { - $entry = [ - $this->user->id, - $rule->id, - 'action', - null, null, null, null, null, null, null, null, null, null, null, null, null, null, - $action->action_type, $action->action_value, $action->order, $action->active, $action->stop_processing, - ]; - $records[] = $entry; - } - } - - // load the CSV document from a string - $csv = Writer::createFromString(); - - // insert the header - try { - $csv->insertOne($header); - } catch (CannotInsertRecord $e) { - throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); - } - - // insert all the records - $csv->insertAll($records); - - try { - $string = $csv->toString(); - } catch (Exception $e) { // intentional generic exception - app('log')->error($e->getMessage()); - - throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); - } - - return $string; - } - - /** - * @throws CannotInsertRecord - * @throws Exception - * @throws FireflyException - */ - private function exportTags(): string - { - $header = ['user_id', 'tag_id', 'created_at', 'updated_at', 'tag', 'date', 'description', 'latitude', 'longitude', 'zoom_level']; - - $tagRepos = app(TagRepositoryInterface::class); - $tagRepos->setUser($this->user); - $tags = $tagRepos->get(); - $records = []; - - /** @var Tag $tag */ - foreach ($tags as $tag) { - $records[] = [ - $this->user->id, - $tag->id, - $tag->created_at->toAtomString(), - $tag->updated_at->toAtomString(), - $tag->tag, - $tag->date?->format('Y-m-d'), - $tag->description, - $tag->latitude, - $tag->longitude, - $tag->zoomLevel, - ]; - } - - // load the CSV document from a string - $csv = Writer::createFromString(); - - // insert the header - try { - $csv->insertOne($header); - } catch (CannotInsertRecord $e) { - throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); - } - - // insert all the records - $csv->insertAll($records); - - try { - $string = $csv->toString(); - } catch (Exception $e) { // intentional generic exception - app('log')->error($e->getMessage()); - - throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); - } - - return $string; - } - - /** - * @throws CannotInsertRecord - * @throws Exception - * @throws FireflyException - */ - private function exportTransactions(): string - { - Log::debug('Will now export transactions.'); - // TODO better place for keys? - $header = ['user_id', 'group_id', 'journal_id', 'created_at', 'updated_at', 'group_title', 'type', 'currency_code', 'amount', 'foreign_currency_code', 'foreign_amount', 'primary_currency_code', 'pc_amount', 'pc_foreign_amount', 'description', 'date', 'source_name', 'source_iban', 'source_type', 'destination_name', 'destination_iban', 'destination_type', 'reconciled', 'category', 'budget', 'bill', 'tags', 'notes']; - - $metaFields = config('firefly.journal_meta_fields'); - $header = array_merge($header, $metaFields); - $primary = Amount::getPrimaryCurrency(); - - $collector = app(GroupCollectorInterface::class); - $collector->setUser($this->user); - $collector->setRange($this->start, $this->end)->withAccountInformation()->withCategoryInformation()->withBillInformation()->withBudgetInformation()->withTagInformation()->withNotes(); - if (0 !== $this->accounts->count()) { - $collector->setAccounts($this->accounts); - } - - $journals = $collector->getExtractedJournals(); - - // get repository for meta data: - $repository = app(TransactionGroupRepositoryInterface::class); - $repository->setUser($this->user); - - $records = []; - - /** @var array $journal */ - foreach ($journals as $journal) { - $metaData = $repository->getMetaFields($journal['transaction_journal_id'], $metaFields); - $amount = Steam::bcround(Steam::negative($journal['amount']), $journal['currency_decimal_places']); - $foreignAmount = null === $journal['foreign_amount'] ? null : Steam::bcround(Steam::negative($journal['foreign_amount']), $journal['foreign_currency_decimal_places']); - $pcAmount = null === $journal['pc_amount'] ? null : Steam::bcround(Steam::negative($journal['pc_amount']), $primary->decimal_places); - $pcForeignAmount = null === $journal['pc_foreign_amount'] ? null : Steam::bcround(Steam::negative($journal['pc_foreign_amount']), $primary->decimal_places); - - if (TransactionTypeEnum::WITHDRAWAL->value !== $journal['transaction_type_type']) { - $amount = Steam::bcround(Steam::positive($journal['amount']), $journal['currency_decimal_places']); - $foreignAmount = null === $journal['foreign_amount'] ? null : Steam::bcround(Steam::positive($journal['foreign_amount']), $journal['foreign_currency_decimal_places']); - $pcAmount = null === $journal['pc_amount'] ? null : Steam::bcround(Steam::positive($journal['pc_amount']), $primary->decimal_places); - $pcForeignAmount = null === $journal['pc_foreign_amount'] ? null : Steam::bcround(Steam::positive($journal['pc_foreign_amount']), $primary->decimal_places); - } - - // opening balance depends on source account type. - if (TransactionTypeEnum::OPENING_BALANCE->value === $journal['transaction_type_type'] && AccountTypeEnum::ASSET->value === $journal['source_account_type']) { - $amount = Steam::bcround(Steam::negative($journal['amount']), $journal['currency_decimal_places']); - $foreignAmount = null === $journal['foreign_amount'] ? null : Steam::bcround(Steam::negative($journal['foreign_amount']), $journal['foreign_currency_decimal_places']); - $pcAmount = null === $journal['pc_amount'] ? null : Steam::bcround(Steam::negative($journal['pc_amount']), $primary->decimal_places); - $pcForeignAmount = null === $journal['pc_foreign_amount'] ? null : Steam::bcround(Steam::negative($journal['pc_foreign_amount']), $primary->decimal_places); - } - - $records[] = [ - $journal['user_id'], $journal['transaction_group_id'], $journal['transaction_journal_id'], $journal['created_at']->toAtomString(), $journal['updated_at']->toAtomString(), $journal['transaction_group_title'], $journal['transaction_type_type'], - // amounts and currencies - $journal['currency_code'], $amount, $journal['foreign_currency_code'], $foreignAmount, $primary->code, $pcAmount, $pcForeignAmount, - - // more fields - $journal['description'], $journal['date']->toAtomString(), $journal['source_account_name'], $journal['source_account_iban'], $journal['source_account_type'], $journal['destination_account_name'], $journal['destination_account_iban'], $journal['destination_account_type'], $journal['reconciled'], $journal['category_name'], $journal['budget_name'], $journal['bill_name'], - $this->mergeTags($journal['tags']), - $this->clearStringKeepNewlines($journal['notes']), - - // sepa - $metaData['sepa_cc'], $metaData['sepa_ct_op'], $metaData['sepa_ct_id'], $metaData['sepa_db'], $metaData['sepa_country'], $metaData['sepa_ep'], $metaData['sepa_ci'], $metaData['sepa_batch_id'], $metaData['external_url'], - - // dates - $metaData['interest_date'], $metaData['book_date'], $metaData['process_date'], $metaData['due_date'], $metaData['payment_date'], $metaData['invoice_date'], - - // others - $metaData['recurrence_id'], $metaData['internal_reference'], $metaData['bunq_payment_id'], $metaData['import_hash'], $metaData['import_hash_v2'], $metaData['external_id'], $metaData['original_source'], - - // recurring transactions - $metaData['recurrence_total'], $metaData['recurrence_count'], - ]; - } - - // load the CSV document from a string - $csv = Writer::createFromString(); - - // insert the header - try { - $csv->insertOne($header); - } catch (CannotInsertRecord $e) { - throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); - } - - // insert all the records - $csv->insertAll($records); - - try { - $string = $csv->toString(); - } catch (Exception $e) { // intentional generic exception - app('log')->error($e->getMessage()); - - throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); - } - - return $string; - } - - public function setAccounts(Collection $accounts): void - { - $this->accounts = $accounts; - } - - private function mergeTags(array $tags): string - { - if (0 === count($tags)) { - return ''; - } - $smol = []; - foreach ($tags as $tag) { - $smol[] = $tag['name']; - } - - return implode(',', $smol); - } - /** * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ @@ -854,6 +157,11 @@ class ExportDataGenerator return null; } + public function setAccounts(Collection $accounts): void + { + $this->accounts = $accounts; + } + public function setEnd(Carbon $end): void { $this->end = $end; @@ -909,8 +217,700 @@ class ExportDataGenerator $this->start = $start; } + public function setUser(User $user): void + { + $this->user = $user; + } + public function setUserGroup(UserGroup $userGroup): void { $this->userGroup = $userGroup; } + + /** + * @throws CannotInsertRecord + * @throws Exception + * @throws FireflyException + */ + private function exportAccounts(): string + { + $header = [ + 'user_id', + 'account_id', + 'created_at', + 'updated_at', + 'type', + 'name', + 'virtual_balance', + 'iban', + 'number', + 'active', + 'currency_code', + 'role', + 'cc_type', + 'cc_payment_date', + 'in_net_worth', + 'interest', + 'interest_period', + ]; + + /** @var AccountRepositoryInterface $repository */ + $repository = app(AccountRepositoryInterface::class); + $repository->setUser($this->user); + $allAccounts = $repository->getAccountsByType([]); + $records = []; + + /** @var Account $account */ + foreach ($allAccounts as $account) { + $currency = $repository->getAccountCurrency($account); + $records[] = [ + $this->user->id, + $account->id, + $account->created_at->toAtomString(), + $account->updated_at->toAtomString(), + $account->accountType->type, + $account->name, + $account->virtual_balance, + $account->iban, + $account->account_number, + $account->active, + $currency?->code, + $repository->getMetaValue($account, 'account_role'), + $repository->getMetaValue($account, 'cc_type'), + $repository->getMetaValue($account, 'cc_monthly_payment_date'), + $repository->getMetaValue($account, 'include_net_worth'), + $repository->getMetaValue($account, 'interest'), + $repository->getMetaValue($account, 'interest_period'), + ]; + } + + // load the CSV document from a string + $csv = Writer::createFromString(); + + // insert the header + try { + $csv->insertOne($header); + } catch (CannotInsertRecord $e) { + throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); + } + + // insert all the records + $csv->insertAll($records); + + try { + $string = $csv->toString(); + } catch (Exception $e) { // intentional generic exception + app('log')->error($e->getMessage()); + + throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); + } + + return $string; + } + + /** + * @throws CannotInsertRecord + * @throws Exception + * @throws FireflyException + */ + private function exportBills(): string + { + /** @var BillRepositoryInterface $repository */ + $repository = app(BillRepositoryInterface::class); + $repository->setUser($this->user); + $bills = $repository->getBills(); + $header = [ + 'user_id', + 'bill_id', + 'created_at', + 'updated_at', + 'currency_code', + 'name', + 'amount_min', + 'amount_max', + 'date', + 'repeat_freq', + 'skip', + 'active', + ]; + $records = []; + + /** @var Bill $bill */ + foreach ($bills as $bill) { + $records[] = [ + $this->user->id, + $bill->id, + $bill->created_at->toAtomString(), + $bill->updated_at->toAtomString(), + $bill->transactionCurrency->code, + $bill->name, + $bill->amount_min, + $bill->amount_max, + $bill->date->format('Y-m-d'), + $bill->repeat_freq, + $bill->skip, + $bill->active, + ]; + } + + // load the CSV document from a string + $csv = Writer::createFromString(); + + // insert the header + try { + $csv->insertOne($header); + } catch (CannotInsertRecord $e) { + throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); + } + + // insert all the records + $csv->insertAll($records); + + try { + $string = $csv->toString(); + } catch (Exception $e) { // intentional generic exception + app('log')->error($e->getMessage()); + + throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); + } + + return $string; + } + + /** + * @throws CannotInsertRecord + * @throws Exception + * @throws FireflyException + */ + private function exportBudgets(): string + { + $header = [ + 'user_id', + 'budget_id', + 'name', + 'active', + 'order', + 'start_date', + 'end_date', + 'currency_code', + 'amount', + ]; + + $budgetRepos = app(BudgetRepositoryInterface::class); + $budgetRepos->setUser($this->user); + $limitRepos = app(BudgetLimitRepositoryInterface::class); + $budgets = $budgetRepos->getBudgets(); + $records = []; + + /** @var Budget $budget */ + foreach ($budgets as $budget) { + $limits = $limitRepos->getBudgetLimits($budget); + + /** @var BudgetLimit $limit */ + foreach ($limits as $limit) { + $records[] = [ + $this->user->id, + $budget->id, + $budget->name, + $budget->active, + $budget->order, + $limit->start_date->format('Y-m-d'), + $limit->end_date->format('Y-m-d'), + $limit->transactionCurrency->code, + $limit->amount, + ]; + } + } + + // load the CSV document from a string + $csv = Writer::createFromString(); + + // insert the header + try { + $csv->insertOne($header); + } catch (CannotInsertRecord $e) { + throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); + } + + // insert all the records + $csv->insertAll($records); + + try { + $string = $csv->toString(); + } catch (Exception $e) { // intentional generic exception + app('log')->error($e->getMessage()); + + throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); + } + + return $string; + } + + /** + * @throws CannotInsertRecord + * @throws Exception + * @throws FireflyException + */ + private function exportCategories(): string + { + $header = ['user_id', 'category_id', 'created_at', 'updated_at', 'name']; + + /** @var CategoryRepositoryInterface $catRepos */ + $catRepos = app(CategoryRepositoryInterface::class); + $catRepos->setUser($this->user); + + $records = []; + $categories = $catRepos->getCategories(); + + /** @var Category $category */ + foreach ($categories as $category) { + $records[] = [ + $this->user->id, + $category->id, + $category->created_at->toAtomString(), + $category->updated_at->toAtomString(), + $category->name, + ]; + } + + // load the CSV document from a string + $csv = Writer::createFromString(); + + // insert the header + try { + $csv->insertOne($header); + } catch (CannotInsertRecord $e) { + throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); + } + + // insert all the records + $csv->insertAll($records); + + try { + $string = $csv->toString(); + } catch (Exception $e) { // intentional generic exception + app('log')->error($e->getMessage()); + + throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); + } + + return $string; + } + + /** + * @throws CannotInsertRecord + * @throws Exception + * @throws FireflyException + */ + private function exportPiggies(): string + { + /** @var PiggyBankRepositoryInterface $piggyRepos */ + $piggyRepos = app(PiggyBankRepositoryInterface::class); + $piggyRepos->setUser($this->user); + + /** @var AccountRepositoryInterface $accountRepos */ + $accountRepos = app(AccountRepositoryInterface::class); + $accountRepos->setUser($this->user); + + $header = [ + 'user_id', + 'piggy_bank_id', + 'created_at', + 'updated_at', + 'account_name', + 'account_type', + 'name', + 'currency_code', + 'target_amount', + 'current_amount', + 'start_date', + 'target_date', + 'order', + 'active', + ]; + $records = []; + $piggies = $piggyRepos->getPiggyBanks(); + + /** @var PiggyBank $piggy */ + foreach ($piggies as $piggy) { + $repetition = $piggyRepos->getRepetition($piggy); + $currency = $accountRepos->getAccountCurrency($piggy->account); + $records[] = [ + $this->user->id, + $piggy->id, + $piggy->created_at->toAtomString(), + $piggy->updated_at->toAtomString(), + $piggy->account->name, + $piggy->account->accountType->type, + $piggy->name, + $currency?->code, + $piggy->target_amount, + $repetition?->current_amount, + $piggy->start_date?->format('Y-m-d'), + $piggy->target_date?->format('Y-m-d'), + $piggy->order, + $piggy->active, + ]; + } + + // load the CSV document from a string + $csv = Writer::createFromString(); + + // insert the header + try { + $csv->insertOne($header); + } catch (CannotInsertRecord $e) { + throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); + } + + // insert all the records + $csv->insertAll($records); + + try { + $string = $csv->toString(); + } catch (Exception $e) { // intentional generic exception + app('log')->error($e->getMessage()); + + throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); + } + + return $string; + } + + /** + * @throws CannotInsertRecord + * @throws Exception + * @throws FireflyException + */ + private function exportRecurring(): string + { + /** @var RecurringRepositoryInterface $recurringRepos */ + $recurringRepos = app(RecurringRepositoryInterface::class); + $recurringRepos->setUser($this->user); + $header = [ + // recurrence: + 'user_id', 'recurrence_id', 'row_contains', 'created_at', 'updated_at', 'type', 'title', 'description', 'first_date', 'repeat_until', 'latest_date', 'repetitions', 'apply_rules', 'active', + + // repetition info: + 'type', 'moment', 'skip', 'weekend', + // transactions + meta: + 'currency_code', 'foreign_currency_code', 'source_name', 'source_type', 'destination_name', 'destination_type', 'amount', 'foreign_amount', 'category', 'budget', 'piggy_bank', 'tags', + ]; + $records = []; + $recurrences = $recurringRepos->get(); + + /** @var Recurrence $recurrence */ + foreach ($recurrences as $recurrence) { + // add recurrence: + $records[] = [ + $this->user->id, $recurrence->id, + 'recurrence', + $recurrence->created_at->toAtomString(), $recurrence->updated_at->toAtomString(), $recurrence->transactionType->type, $recurrence->title, $recurrence->description, $recurrence->first_date?->format('Y-m-d'), $recurrence->repeat_until?->format('Y-m-d'), $recurrence->latest_date?->format('Y-m-d'), $recurrence->repetitions, $recurrence->apply_rules, $recurrence->active, + ]; + + // add new row for each repetition + /** @var RecurrenceRepetition $repetition */ + foreach ($recurrence->recurrenceRepetitions as $repetition) { + $records[] = [ + // recurrence + $this->user->id, + $recurrence->id, + 'repetition', + null, null, null, null, null, null, null, null, null, null, null, + + // repetition: + $repetition->repetition_type, $repetition->repetition_moment, $repetition->repetition_skip, $repetition->weekend, + ]; + } + + /** @var RecurrenceTransaction $transaction */ + foreach ($recurrence->recurrenceTransactions as $transaction) { + $categoryName = $recurringRepos->getCategoryName($transaction); + $budgetId = $recurringRepos->getBudget($transaction); + $piggyBankId = $recurringRepos->getPiggyBank($transaction); + $tags = $recurringRepos->getTags($transaction); + + $records[] = [ + // recurrence + $this->user->id, + $recurrence->id, + 'transaction', + null, null, null, null, null, null, null, null, null, null, null, + + // repetition: + null, null, null, null, + + // transaction: + $transaction->transactionCurrency->code, $transaction->foreignCurrency?->code, $transaction->sourceAccount->name, $transaction->sourceAccount->accountType->type, $transaction->destinationAccount->name, $transaction->destinationAccount->accountType->type, $transaction->amount, $transaction->foreign_amount, $categoryName, $budgetId, $piggyBankId, implode(',', $tags), + ]; + } + } + // load the CSV document from a string + $csv = Writer::createFromString(); + + // insert the header + try { + $csv->insertOne($header); + } catch (CannotInsertRecord $e) { + throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); + } + + // insert all the records + $csv->insertAll($records); + + try { + $string = $csv->toString(); + } catch (Exception $e) { // intentional generic exception + app('log')->error($e->getMessage()); + + throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); + } + + return $string; + } + + /** + * @throws CannotInsertRecord + * @throws Exception + * @throws FireflyException + */ + private function exportRules(): string + { + $header = [ + 'user_id', 'rule_id', 'row_contains', + 'created_at', 'updated_at', 'group_id', 'title', 'description', 'order', 'active', 'stop_processing', 'strict', + 'trigger_type', 'trigger_value', 'trigger_order', 'trigger_active', 'trigger_stop_processing', + 'action_type', 'action_value', 'action_order', 'action_active', 'action_stop_processing']; + $ruleRepos = app(RuleRepositoryInterface::class); + $ruleRepos->setUser($this->user); + $rules = $ruleRepos->getAll(); + $records = []; + + /** @var Rule $rule */ + foreach ($rules as $rule) { + $entry = [ + $this->user->id, $rule->id, + 'rule', + $rule->created_at->toAtomString(), $rule->updated_at->toAtomString(), $rule->ruleGroup->id, $rule->ruleGroup->title, $rule->title, $rule->description, $rule->order, $rule->active, $rule->stop_processing, $rule->strict, + null, null, null, null, null, null, null, null, null, + ]; + $records[] = $entry; + + /** @var RuleTrigger $trigger */ + foreach ($rule->ruleTriggers as $trigger) { + $entry = [ + $this->user->id, + $rule->id, + 'trigger', + null, null, null, null, null, null, null, null, null, + $trigger->trigger_type, $trigger->trigger_value, $trigger->order, $trigger->active, $trigger->stop_processing, + null, null, null, null, null, + ]; + $records[] = $entry; + } + + /** @var RuleAction $action */ + foreach ($rule->ruleActions as $action) { + $entry = [ + $this->user->id, + $rule->id, + 'action', + null, null, null, null, null, null, null, null, null, null, null, null, null, null, + $action->action_type, $action->action_value, $action->order, $action->active, $action->stop_processing, + ]; + $records[] = $entry; + } + } + + // load the CSV document from a string + $csv = Writer::createFromString(); + + // insert the header + try { + $csv->insertOne($header); + } catch (CannotInsertRecord $e) { + throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); + } + + // insert all the records + $csv->insertAll($records); + + try { + $string = $csv->toString(); + } catch (Exception $e) { // intentional generic exception + app('log')->error($e->getMessage()); + + throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); + } + + return $string; + } + + /** + * @throws CannotInsertRecord + * @throws Exception + * @throws FireflyException + */ + private function exportTags(): string + { + $header = ['user_id', 'tag_id', 'created_at', 'updated_at', 'tag', 'date', 'description', 'latitude', 'longitude', 'zoom_level']; + + $tagRepos = app(TagRepositoryInterface::class); + $tagRepos->setUser($this->user); + $tags = $tagRepos->get(); + $records = []; + + /** @var Tag $tag */ + foreach ($tags as $tag) { + $records[] = [ + $this->user->id, + $tag->id, + $tag->created_at->toAtomString(), + $tag->updated_at->toAtomString(), + $tag->tag, + $tag->date?->format('Y-m-d'), + $tag->description, + $tag->latitude, + $tag->longitude, + $tag->zoomLevel, + ]; + } + + // load the CSV document from a string + $csv = Writer::createFromString(); + + // insert the header + try { + $csv->insertOne($header); + } catch (CannotInsertRecord $e) { + throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); + } + + // insert all the records + $csv->insertAll($records); + + try { + $string = $csv->toString(); + } catch (Exception $e) { // intentional generic exception + app('log')->error($e->getMessage()); + + throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); + } + + return $string; + } + + /** + * @throws CannotInsertRecord + * @throws Exception + * @throws FireflyException + */ + private function exportTransactions(): string + { + Log::debug('Will now export transactions.'); + // TODO better place for keys? + $header = ['user_id', 'group_id', 'journal_id', 'created_at', 'updated_at', 'group_title', 'type', 'currency_code', 'amount', 'foreign_currency_code', 'foreign_amount', 'primary_currency_code', 'pc_amount', 'pc_foreign_amount', 'description', 'date', 'source_name', 'source_iban', 'source_type', 'destination_name', 'destination_iban', 'destination_type', 'reconciled', 'category', 'budget', 'bill', 'tags', 'notes']; + + $metaFields = config('firefly.journal_meta_fields'); + $header = array_merge($header, $metaFields); + $primary = Amount::getPrimaryCurrency(); + + $collector = app(GroupCollectorInterface::class); + $collector->setUser($this->user); + $collector->setRange($this->start, $this->end)->withAccountInformation()->withCategoryInformation()->withBillInformation()->withBudgetInformation()->withTagInformation()->withNotes(); + if (0 !== $this->accounts->count()) { + $collector->setAccounts($this->accounts); + } + + $journals = $collector->getExtractedJournals(); + + // get repository for meta data: + $repository = app(TransactionGroupRepositoryInterface::class); + $repository->setUser($this->user); + + $records = []; + + /** @var array $journal */ + foreach ($journals as $journal) { + $metaData = $repository->getMetaFields($journal['transaction_journal_id'], $metaFields); + $amount = Steam::bcround(Steam::negative($journal['amount']), $journal['currency_decimal_places']); + $foreignAmount = null === $journal['foreign_amount'] ? null : Steam::bcround(Steam::negative($journal['foreign_amount']), $journal['foreign_currency_decimal_places']); + $pcAmount = null === $journal['pc_amount'] ? null : Steam::bcround(Steam::negative($journal['pc_amount']), $primary->decimal_places); + $pcForeignAmount = null === $journal['pc_foreign_amount'] ? null : Steam::bcround(Steam::negative($journal['pc_foreign_amount']), $primary->decimal_places); + + if (TransactionTypeEnum::WITHDRAWAL->value !== $journal['transaction_type_type']) { + $amount = Steam::bcround(Steam::positive($journal['amount']), $journal['currency_decimal_places']); + $foreignAmount = null === $journal['foreign_amount'] ? null : Steam::bcround(Steam::positive($journal['foreign_amount']), $journal['foreign_currency_decimal_places']); + $pcAmount = null === $journal['pc_amount'] ? null : Steam::bcround(Steam::positive($journal['pc_amount']), $primary->decimal_places); + $pcForeignAmount = null === $journal['pc_foreign_amount'] ? null : Steam::bcround(Steam::positive($journal['pc_foreign_amount']), $primary->decimal_places); + } + + // opening balance depends on source account type. + if (TransactionTypeEnum::OPENING_BALANCE->value === $journal['transaction_type_type'] && AccountTypeEnum::ASSET->value === $journal['source_account_type']) { + $amount = Steam::bcround(Steam::negative($journal['amount']), $journal['currency_decimal_places']); + $foreignAmount = null === $journal['foreign_amount'] ? null : Steam::bcround(Steam::negative($journal['foreign_amount']), $journal['foreign_currency_decimal_places']); + $pcAmount = null === $journal['pc_amount'] ? null : Steam::bcround(Steam::negative($journal['pc_amount']), $primary->decimal_places); + $pcForeignAmount = null === $journal['pc_foreign_amount'] ? null : Steam::bcround(Steam::negative($journal['pc_foreign_amount']), $primary->decimal_places); + } + + $records[] = [ + $journal['user_id'], $journal['transaction_group_id'], $journal['transaction_journal_id'], $journal['created_at']->toAtomString(), $journal['updated_at']->toAtomString(), $journal['transaction_group_title'], $journal['transaction_type_type'], + // amounts and currencies + $journal['currency_code'], $amount, $journal['foreign_currency_code'], $foreignAmount, $primary->code, $pcAmount, $pcForeignAmount, + + // more fields + $journal['description'], $journal['date']->toAtomString(), $journal['source_account_name'], $journal['source_account_iban'], $journal['source_account_type'], $journal['destination_account_name'], $journal['destination_account_iban'], $journal['destination_account_type'], $journal['reconciled'], $journal['category_name'], $journal['budget_name'], $journal['bill_name'], + $this->mergeTags($journal['tags']), + $this->clearStringKeepNewlines($journal['notes']), + + // sepa + $metaData['sepa_cc'], $metaData['sepa_ct_op'], $metaData['sepa_ct_id'], $metaData['sepa_db'], $metaData['sepa_country'], $metaData['sepa_ep'], $metaData['sepa_ci'], $metaData['sepa_batch_id'], $metaData['external_url'], + + // dates + $metaData['interest_date'], $metaData['book_date'], $metaData['process_date'], $metaData['due_date'], $metaData['payment_date'], $metaData['invoice_date'], + + // others + $metaData['recurrence_id'], $metaData['internal_reference'], $metaData['bunq_payment_id'], $metaData['import_hash'], $metaData['import_hash_v2'], $metaData['external_id'], $metaData['original_source'], + + // recurring transactions + $metaData['recurrence_total'], $metaData['recurrence_count'], + ]; + } + + // load the CSV document from a string + $csv = Writer::createFromString(); + + // insert the header + try { + $csv->insertOne($header); + } catch (CannotInsertRecord $e) { + throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); + } + + // insert all the records + $csv->insertAll($records); + + try { + $string = $csv->toString(); + } catch (Exception $e) { // intentional generic exception + app('log')->error($e->getMessage()); + + throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); + } + + return $string; + } + + private function mergeTags(array $tags): string + { + if (0 === count($tags)) { + return ''; + } + $smol = []; + foreach ($tags as $tag) { + $smol[] = $tag['name']; + } + + return implode(',', $smol); + } } diff --git a/app/Support/FireflyConfig.php b/app/Support/FireflyConfig.php index 499b123602..b79967f8cb 100644 --- a/app/Support/FireflyConfig.php +++ b/app/Support/FireflyConfig.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Support; +use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Configuration; use Illuminate\Contracts\Encryption\DecryptException; @@ -30,7 +31,6 @@ use Illuminate\Contracts\Encryption\EncryptException; use Illuminate\Database\QueryException; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Log; -use Exception; /** * Class FireflyConfig. @@ -39,16 +39,43 @@ class FireflyConfig { public function delete(string $name): void { - $fullName = 'ff3-config-'.$name; + $fullName = 'ff3-config-' . $name; if (Cache::has($fullName)) { Cache::forget($fullName); } Configuration::where('name', $name)->forceDelete(); } - public function has(string $name): bool + /** + * @param null|bool|int|string $default + * + * @throws FireflyException + */ + public function get(string $name, mixed $default = null): ?Configuration { - return 1 === Configuration::where('name', $name)->count(); + $fullName = 'ff3-config-' . $name; + if (Cache::has($fullName)) { + return Cache::get($fullName); + } + + try { + /** @var null|Configuration $config */ + $config = Configuration::where('name', $name)->first(['id', 'name', 'data']); + } catch (Exception | QueryException $e) { + throw new FireflyException(sprintf('Could not poll the database: %s', $e->getMessage()), 0, $e); + } + + if (null !== $config) { + Cache::forever($fullName, $config); + + return $config; + } + // no preference found and default is null: + if (null === $default) { + return null; + } + + return $this->set($name, $default); } public function getEncrypted(string $name, mixed $default = null): ?Configuration @@ -74,28 +101,10 @@ class FireflyConfig return $result; } - /** - * @param null|bool|int|string $default - * - * @throws FireflyException - */ - public function get(string $name, mixed $default = null): ?Configuration + public function getFresh(string $name, mixed $default = null): ?Configuration { - $fullName = 'ff3-config-'.$name; - if (Cache::has($fullName)) { - return Cache::get($fullName); - } - - try { - /** @var null|Configuration $config */ - $config = Configuration::where('name', $name)->first(['id', 'name', 'data']); - } catch (Exception|QueryException $e) { - throw new FireflyException(sprintf('Could not poll the database: %s', $e->getMessage()), 0, $e); - } - + $config = Configuration::where('name', $name)->first(['id', 'name', 'data']); if (null !== $config) { - Cache::forever($fullName, $config); - return $config; } // no preference found and default is null: @@ -106,6 +115,19 @@ class FireflyConfig return $this->set($name, $default); } + public function has(string $name): bool + { + return 1 === Configuration::where('name', $name)->count(); + } + + /** + * @param mixed $value + */ + public function put(string $name, $value): Configuration + { + return $this->set($name, $value); + } + public function set(string $name, mixed $value): Configuration { try { @@ -124,39 +146,17 @@ class FireflyConfig $item->name = $name; $item->data = $value; $item->save(); - Cache::forget('ff3-config-'.$name); + Cache::forget('ff3-config-' . $name); return $item; } $config->data = $value; $config->save(); - Cache::forget('ff3-config-'.$name); + Cache::forget('ff3-config-' . $name); return $config; } - public function getFresh(string $name, mixed $default = null): ?Configuration - { - $config = Configuration::where('name', $name)->first(['id', 'name', 'data']); - if (null !== $config) { - return $config; - } - // no preference found and default is null: - if (null === $default) { - return null; - } - - return $this->set($name, $default); - } - - /** - * @param mixed $value - */ - public function put(string $name, $value): Configuration - { - return $this->set($name, $value); - } - public function setEncrypted(string $name, mixed $value): Configuration { try { diff --git a/app/Support/Form/AccountForm.php b/app/Support/Form/AccountForm.php index 19a043c76d..c7a7685061 100644 --- a/app/Support/Form/AccountForm.php +++ b/app/Support/Form/AccountForm.php @@ -51,55 +51,24 @@ class AccountForm $repository = $this->getAccountRepository(); $grouped = $this->getAccountsGrouped($types, $repository); $cash = $repository->getCashAccount(); - $key = (string) trans('firefly.cash_account_type'); - $grouped[$key][$cash->id] = sprintf('(%s)', (string) trans('firefly.cash')); + $key = (string)trans('firefly.cash_account_type'); + $grouped[$key][$cash->id] = sprintf('(%s)', (string)trans('firefly.cash')); return $this->select($name, $grouped, $value, $options); } - private function getAccountsGrouped(array $types, ?AccountRepositoryInterface $repository = null): array - { - if (!$repository instanceof AccountRepositoryInterface) { - $repository = $this->getAccountRepository(); - } - $accountList = $repository->getActiveAccountsByType($types); - $liabilityTypes = [AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::LOAN->value]; - $grouped = []; - - /** @var Account $account */ - foreach ($accountList as $account) { - $role = (string) $repository->getMetaValue($account, 'account_role'); - if (in_array($account->accountType->type, $liabilityTypes, true)) { - $role = sprintf('l_%s', $account->accountType->type); - } - if ('' === $role) { - $role = 'no_account_type'; - if (AccountTypeEnum::EXPENSE->value === $account->accountType->type) { - $role = 'expense_account'; - } - if (AccountTypeEnum::REVENUE->value === $account->accountType->type) { - $role = 'revenue_account'; - } - } - $key = (string) trans(sprintf('firefly.opt_group_%s', $role)); - $grouped[$key][$account->id] = $account->name; - } - - return $grouped; - } - /** * Grouped dropdown list of all accounts that are valid as the destination of a withdrawal. */ public function activeWithdrawalDestinations(string $name, mixed $value = null, ?array $options = null): string { - $types = [AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::EXPENSE->value]; - $repository = $this->getAccountRepository(); - $grouped = $this->getAccountsGrouped($types, $repository); + $types = [AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::EXPENSE->value]; + $repository = $this->getAccountRepository(); + $grouped = $this->getAccountsGrouped($types, $repository); $cash = $repository->getCashAccount(); - $key = (string) trans('firefly.cash_account_type'); - $grouped[$key][$cash->id] = sprintf('(%s)', (string) trans('firefly.cash')); + $key = (string)trans('firefly.cash_account_type'); + $grouped[$key][$cash->id] = sprintf('(%s)', (string)trans('firefly.cash')); return $this->select($name, $grouped, $value, $options); } @@ -111,15 +80,15 @@ class AccountForm */ public function assetAccountCheckList(string $name, ?array $options = null): string { - $options ??= []; + $options ??= []; $label = $this->label($name, $options); $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); $selected = request()->old($name) ?? []; // get all asset accounts: - $types = [AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value]; - $grouped = $this->getAccountsGrouped($types); + $types = [AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value]; + $grouped = $this->getAccountsGrouped($types); unset($options['class']); @@ -173,4 +142,35 @@ class AccountForm return $this->select($name, $grouped, $value, $options); } + + private function getAccountsGrouped(array $types, ?AccountRepositoryInterface $repository = null): array + { + if (!$repository instanceof AccountRepositoryInterface) { + $repository = $this->getAccountRepository(); + } + $accountList = $repository->getActiveAccountsByType($types); + $liabilityTypes = [AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::LOAN->value]; + $grouped = []; + + /** @var Account $account */ + foreach ($accountList as $account) { + $role = (string)$repository->getMetaValue($account, 'account_role'); + if (in_array($account->accountType->type, $liabilityTypes, true)) { + $role = sprintf('l_%s', $account->accountType->type); + } + if ('' === $role) { + $role = 'no_account_type'; + if (AccountTypeEnum::EXPENSE->value === $account->accountType->type) { + $role = 'expense_account'; + } + if (AccountTypeEnum::REVENUE->value === $account->accountType->type) { + $role = 'revenue_account'; + } + } + $key = (string)trans(sprintf('firefly.opt_group_%s', $role)); + $grouped[$key][$account->id] = $account->name; + } + + return $grouped; + } } diff --git a/app/Support/Form/CurrencyForm.php b/app/Support/Form/CurrencyForm.php index 88816667d4..6d24780f19 100644 --- a/app/Support/Form/CurrencyForm.php +++ b/app/Support/Form/CurrencyForm.php @@ -49,60 +49,6 @@ class CurrencyForm return $this->currencyField($name, 'amount', $value, $options); } - /** - * @phpstan-param view-string $view - * - * @throws FireflyException - */ - protected function currencyField(string $name, string $view, mixed $value = null, ?array $options = null): string - { - $label = $this->label($name, $options); - $options = $this->expandOptionArray($name, $label, $options); - $classes = $this->getHolderClasses($name); - $value = $this->fillFieldValue($name, $value); - $options['step'] = 'any'; - $primaryCurrency = $options['currency'] ?? app('amount')->getPrimaryCurrency(); - - /** @var Collection $currencies */ - $currencies = app('amount')->getCurrencies(); - unset($options['currency'], $options['placeholder']); - // perhaps the currency has been sent to us in the field $amount_currency_id_$name (amount_currency_id_amount) - $preFilled = session('preFilled'); - if (!is_array($preFilled)) { - $preFilled = []; - } - $key = 'amount_currency_id_'.$name; - $sentCurrencyId = array_key_exists($key, $preFilled) ? (int) $preFilled[$key] : $primaryCurrency->id; - - app('log')->debug(sprintf('Sent currency ID is %d', $sentCurrencyId)); - - // find this currency in set of currencies: - foreach ($currencies as $currency) { - if ($currency->id === $sentCurrencyId) { - $primaryCurrency = $currency; - app('log')->debug(sprintf('default currency is now %s', $primaryCurrency->code)); - - break; - } - } - - // make sure value is formatted nicely: - if (null !== $value && '' !== $value) { - $value = app('steam')->bcround($value, $primaryCurrency->decimal_places); - } - - try { - $html = view('form.'.$view, compact('primaryCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); - } catch (Throwable $e) { - app('log')->debug(sprintf('Could not render currencyField(): %s', $e->getMessage())); - $html = 'Could not render currencyField.'; - - throw new FireflyException($html, 0, $e); - } - - return $html; - } - /** * TODO describe and cleanup. * @@ -115,6 +61,52 @@ class CurrencyForm return $this->allCurrencyField($name, 'balance', $value, $options); } + /** + * TODO cleanup and describe + * + * @param mixed $value + */ + public function currencyList(string $name, $value = null, ?array $options = null): string + { + /** @var CurrencyRepositoryInterface $currencyRepos */ + $currencyRepos = app(CurrencyRepositoryInterface::class); + + // get all currencies: + $list = $currencyRepos->get(); + $array = []; + + /** @var TransactionCurrency $currency */ + foreach ($list as $currency) { + $array[$currency->id] = $currency->name . ' (' . $currency->symbol . ')'; + } + + return $this->select($name, $array, $value, $options); + } + + /** + * TODO cleanup and describe + * + * @param mixed $value + */ + public function currencyListEmpty(string $name, $value = null, ?array $options = null): string + { + /** @var CurrencyRepositoryInterface $currencyRepos */ + $currencyRepos = app(CurrencyRepositoryInterface::class); + + // get all currencies: + $list = $currencyRepos->get(); + $array = [ + 0 => (string)trans('firefly.no_currency'), + ]; + + /** @var TransactionCurrency $currency */ + foreach ($list as $currency) { + $array[$currency->id] = $currency->name . ' (' . $currency->symbol . ')'; + } + + return $this->select($name, $array, $value, $options); + } + /** * TODO describe and cleanup * @@ -132,16 +124,16 @@ class CurrencyForm $primaryCurrency = $options['currency'] ?? app('amount')->getPrimaryCurrency(); /** @var Collection $currencies */ - $currencies = app('amount')->getAllCurrencies(); + $currencies = app('amount')->getAllCurrencies(); unset($options['currency'], $options['placeholder']); // perhaps the currency has been sent to us in the field $amount_currency_id_$name (amount_currency_id_amount) - $preFilled = session('preFilled'); + $preFilled = session('preFilled'); if (!is_array($preFilled)) { $preFilled = []; } - $key = 'amount_currency_id_'.$name; - $sentCurrencyId = array_key_exists($key, $preFilled) ? (int) $preFilled[$key] : $primaryCurrency->id; + $key = 'amount_currency_id_' . $name; + $sentCurrencyId = array_key_exists($key, $preFilled) ? (int)$preFilled[$key] : $primaryCurrency->id; app('log')->debug(sprintf('Sent currency ID is %d', $sentCurrencyId)); @@ -161,7 +153,7 @@ class CurrencyForm } try { - $html = view('form.'.$view, compact('primaryCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); + $html = view('form.' . $view, compact('primaryCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { app('log')->debug(sprintf('Could not render currencyField(): %s', $e->getMessage())); $html = 'Could not render currencyField.'; @@ -173,48 +165,56 @@ class CurrencyForm } /** - * TODO cleanup and describe + * @phpstan-param view-string $view * - * @param mixed $value + * @throws FireflyException */ - public function currencyList(string $name, $value = null, ?array $options = null): string + protected function currencyField(string $name, string $view, mixed $value = null, ?array $options = null): string { - /** @var CurrencyRepositoryInterface $currencyRepos */ - $currencyRepos = app(CurrencyRepositoryInterface::class); + $label = $this->label($name, $options); + $options = $this->expandOptionArray($name, $label, $options); + $classes = $this->getHolderClasses($name); + $value = $this->fillFieldValue($name, $value); + $options['step'] = 'any'; + $primaryCurrency = $options['currency'] ?? app('amount')->getPrimaryCurrency(); - // get all currencies: - $list = $currencyRepos->get(); - $array = []; + /** @var Collection $currencies */ + $currencies = app('amount')->getCurrencies(); + unset($options['currency'], $options['placeholder']); + // perhaps the currency has been sent to us in the field $amount_currency_id_$name (amount_currency_id_amount) + $preFilled = session('preFilled'); + if (!is_array($preFilled)) { + $preFilled = []; + } + $key = 'amount_currency_id_' . $name; + $sentCurrencyId = array_key_exists($key, $preFilled) ? (int)$preFilled[$key] : $primaryCurrency->id; - /** @var TransactionCurrency $currency */ - foreach ($list as $currency) { - $array[$currency->id] = $currency->name.' ('.$currency->symbol.')'; + app('log')->debug(sprintf('Sent currency ID is %d', $sentCurrencyId)); + + // find this currency in set of currencies: + foreach ($currencies as $currency) { + if ($currency->id === $sentCurrencyId) { + $primaryCurrency = $currency; + app('log')->debug(sprintf('default currency is now %s', $primaryCurrency->code)); + + break; + } } - return $this->select($name, $array, $value, $options); - } - - /** - * TODO cleanup and describe - * - * @param mixed $value - */ - public function currencyListEmpty(string $name, $value = null, ?array $options = null): string - { - /** @var CurrencyRepositoryInterface $currencyRepos */ - $currencyRepos = app(CurrencyRepositoryInterface::class); - - // get all currencies: - $list = $currencyRepos->get(); - $array = [ - 0 => (string) trans('firefly.no_currency'), - ]; - - /** @var TransactionCurrency $currency */ - foreach ($list as $currency) { - $array[$currency->id] = $currency->name.' ('.$currency->symbol.')'; + // make sure value is formatted nicely: + if (null !== $value && '' !== $value) { + $value = app('steam')->bcround($value, $primaryCurrency->decimal_places); } - return $this->select($name, $array, $value, $options); + try { + $html = view('form.' . $view, compact('primaryCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); + } catch (Throwable $e) { + app('log')->debug(sprintf('Could not render currencyField(): %s', $e->getMessage())); + $html = 'Could not render currencyField.'; + + throw new FireflyException($html, 0, $e); + } + + return $html; } } diff --git a/app/Support/Form/FormSupport.php b/app/Support/Form/FormSupport.php index 4bcc0fcb87..e41c785f00 100644 --- a/app/Support/Form/FormSupport.php +++ b/app/Support/Form/FormSupport.php @@ -36,7 +36,7 @@ trait FormSupport { public function multiSelect(string $name, ?array $list = null, mixed $selected = null, ?array $options = null): string { - $list ??= []; + $list ??= []; $label = $this->label($name, $options); $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); @@ -54,15 +54,26 @@ trait FormSupport return $html; } - protected function label(string $name, ?array $options = null): string + /** + * @param mixed $selected + */ + public function select(string $name, ?array $list = null, $selected = null, ?array $options = null): string { - $options ??= []; - if (array_key_exists('label', $options)) { - return $options['label']; - } - $name = str_replace('[]', '', $name); + $list ??= []; + $label = $this->label($name, $options); + $options = $this->expandOptionArray($name, $label, $options); + $classes = $this->getHolderClasses($name); + $selected = $this->fillFieldValue($name, $selected); + unset($options['autocomplete'], $options['placeholder']); - return (string)trans('form.'.$name); + try { + $html = view('form.select', compact('classes', 'name', 'label', 'selected', 'options', 'list'))->render(); + } catch (Throwable $e) { + app('log')->debug(sprintf('Could not render select(): %s', $e->getMessage())); + $html = 'Could not render select.'; + } + + return $html; } /** @@ -70,29 +81,16 @@ trait FormSupport */ protected function expandOptionArray(string $name, $label, ?array $options = null): array { - $options ??= []; + $options ??= []; $name = str_replace('[]', '', $name); $options['class'] = 'form-control'; - $options['id'] = 'ffInput_'.$name; + $options['id'] = 'ffInput_' . $name; $options['autocomplete'] = 'off'; $options['placeholder'] = ucfirst((string)$label); return $options; } - protected function getHolderClasses(string $name): string - { - // Get errors from session: - /** @var null|MessageBag $errors */ - $errors = session('errors'); - - if (null !== $errors && $errors->has($name)) { - return 'form-group has-error has-feedback'; - } - - return 'form-group'; - } - /** * @param null|mixed $value * @@ -116,28 +114,6 @@ trait FormSupport return $value; } - /** - * @param mixed $selected - */ - public function select(string $name, ?array $list = null, $selected = null, ?array $options = null): string - { - $list ??= []; - $label = $this->label($name, $options); - $options = $this->expandOptionArray($name, $label, $options); - $classes = $this->getHolderClasses($name); - $selected = $this->fillFieldValue($name, $selected); - unset($options['autocomplete'], $options['placeholder']); - - try { - $html = view('form.select', compact('classes', 'name', 'label', 'selected', 'options', 'list'))->render(); - } catch (Throwable $e) { - app('log')->debug(sprintf('Could not render select(): %s', $e->getMessage())); - $html = 'Could not render select.'; - } - - return $html; - } - protected function getAccountRepository(): AccountRepositoryInterface { return app(AccountRepositoryInterface::class); @@ -147,4 +123,28 @@ trait FormSupport { return today(config('app.timezone')); } + + protected function getHolderClasses(string $name): string + { + // Get errors from session: + /** @var null|MessageBag $errors */ + $errors = session('errors'); + + if (null !== $errors && $errors->has($name)) { + return 'form-group has-error has-feedback'; + } + + return 'form-group'; + } + + protected function label(string $name, ?array $options = null): string + { + $options ??= []; + if (array_key_exists('label', $options)) { + return $options['label']; + } + $name = str_replace('[]', '', $name); + + return (string)trans('form.' . $name); + } } diff --git a/app/Support/Form/PiggyBankForm.php b/app/Support/Form/PiggyBankForm.php index 78919b30bf..b6233fd0d6 100644 --- a/app/Support/Form/PiggyBankForm.php +++ b/app/Support/Form/PiggyBankForm.php @@ -47,7 +47,7 @@ class PiggyBankForm /** @var PiggyBankRepositoryInterface $repository */ $repository = app(PiggyBankRepositoryInterface::class); $piggyBanks = $repository->getPiggyBanksWithAmount(); - $title = (string) trans('firefly.default_group_title_name'); + $title = (string)trans('firefly.default_group_title_name'); $array = []; $subList = [ 0 => [ @@ -55,21 +55,21 @@ class PiggyBankForm 'title' => $title, ], 'piggies' => [ - (string) trans('firefly.none_in_select_list'), + (string)trans('firefly.none_in_select_list'), ], ], ]; /** @var PiggyBank $piggy */ foreach ($piggyBanks as $piggy) { - $group = $piggy->objectGroups->first(); - $groupTitle = null; - $groupOrder = 0; + $group = $piggy->objectGroups->first(); + $groupTitle = null; + $groupOrder = 0; if (null !== $group) { $groupTitle = $group->title; $groupOrder = $group->order; } - $subList[$groupOrder] ??= [ + $subList[$groupOrder] ??= [ 'group' => [ 'title' => $groupTitle, ], diff --git a/app/Support/Form/RuleForm.php b/app/Support/Form/RuleForm.php index 6baee553be..f635ed9b86 100644 --- a/app/Support/Form/RuleForm.php +++ b/app/Support/Form/RuleForm.php @@ -41,8 +41,8 @@ class RuleForm $groupRepos = app(RuleGroupRepositoryInterface::class); // get all currencies: - $list = $groupRepos->get(); - $array = []; + $list = $groupRepos->get(); + $array = []; /** @var RuleGroup $group */ foreach ($list as $group) { @@ -57,21 +57,21 @@ class RuleForm */ public function ruleGroupListWithEmpty(string $name, $value = null, ?array $options = null): string { - $options ??= []; + $options ??= []; $options['class'] = 'form-control'; /** @var RuleGroupRepositoryInterface $groupRepos */ - $groupRepos = app(RuleGroupRepositoryInterface::class); + $groupRepos = app(RuleGroupRepositoryInterface::class); // get all currencies: - $list = $groupRepos->get(); - $array = [ - 0 => (string) trans('firefly.none_in_select_list'), + $list = $groupRepos->get(); + $array = [ + 0 => (string)trans('firefly.none_in_select_list'), ]; /** @var RuleGroup $group */ foreach ($list as $group) { - if (array_key_exists('hidden', $options) && (int) $options['hidden'] !== $group->id) { + if (array_key_exists('hidden', $options) && (int)$options['hidden'] !== $group->id) { $array[$group->id] = $group->title; } } diff --git a/app/Support/Http/Api/AccountBalanceGrouped.php b/app/Support/Http/Api/AccountBalanceGrouped.php index d1f7915d23..4c60e00870 100644 --- a/app/Support/Http/Api/AccountBalanceGrouped.php +++ b/app/Support/Http/Api/AccountBalanceGrouped.php @@ -146,114 +146,6 @@ class AccountBalanceGrouped $converter->summarize(); } - private function processJournal(array $journal): void - { - // format the date according to the period - $period = $journal['date']->format($this->carbonFormat); - $currencyId = (int)$journal['currency_id']; - $currency = $this->findCurrency($currencyId); - - // set the array with monetary info, if it does not exist. - $this->createDefaultDataEntry($journal); - // set the array (in monetary info) with spent/earned in this $period, if it does not exist. - $this->createDefaultPeriodEntry($journal); - - // is this journal's amount in- our outgoing? - $key = $this->getDataKey($journal); - $amount = 'spent' === $key ? Steam::negative($journal['amount']) : Steam::positive($journal['amount']); - - // get conversion rate - $rate = $this->getRate($currency, $journal['date']); - $amountConverted = bcmul($amount, $rate); - - // perhaps transaction already has the foreign amount in the primary currency. - if ((int)$journal['foreign_currency_id'] === $this->primary->id) { - $amountConverted = $journal['foreign_amount'] ?? '0'; - $amountConverted = 'earned' === $key ? Steam::positive($amountConverted) : Steam::negative($amountConverted); - } - - // add normal entry - $this->data[$currencyId][$period][$key] = bcadd((string)$this->data[$currencyId][$period][$key], $amount); - - // add converted entry - $convertedKey = sprintf('pc_%s', $key); - $this->data[$currencyId][$period][$convertedKey] = bcadd((string)$this->data[$currencyId][$period][$convertedKey], $amountConverted); - } - - private function findCurrency(int $currencyId): TransactionCurrency - { - if (array_key_exists($currencyId, $this->currencies)) { - return $this->currencies[$currencyId]; - } - $this->currencies[$currencyId] = Amount::getTransactionCurrencyById($currencyId); - - return $this->currencies[$currencyId]; - } - - private function createDefaultDataEntry(array $journal): void - { - $currencyId = (int)$journal['currency_id']; - $this->data[$currencyId] ??= [ - 'currency_id' => (string)$currencyId, - 'currency_symbol' => $journal['currency_symbol'], - 'currency_code' => $journal['currency_code'], - 'currency_name' => $journal['currency_name'], - 'currency_decimal_places' => $journal['currency_decimal_places'], - // primary currency info (could be the same) - 'primary_currency_id' => (string)$this->primary->id, - 'primary_currency_code' => $this->primary->code, - 'primary_currency_symbol' => $this->primary->symbol, - 'primary_currency_decimal_places' => $this->primary->decimal_places, - ]; - } - - private function createDefaultPeriodEntry(array $journal): void - { - $currencyId = (int)$journal['currency_id']; - $period = $journal['date']->format($this->carbonFormat); - $this->data[$currencyId][$period] ??= [ - 'period' => $period, - 'spent' => '0', - 'earned' => '0', - 'pc_spent' => '0', - 'pc_earned' => '0', - ]; - } - - private function getDataKey(array $journal): string - { - // deposit = incoming - // transfer or reconcile or opening balance, and these accounts are the destination. - if ( - TransactionTypeEnum::DEPOSIT->value === $journal['transaction_type_type'] - - || ( - ( - TransactionTypeEnum::TRANSFER->value === $journal['transaction_type_type'] - || TransactionTypeEnum::RECONCILIATION->value === $journal['transaction_type_type'] - || TransactionTypeEnum::OPENING_BALANCE->value === $journal['transaction_type_type'] - ) - && in_array($journal['destination_account_id'], $this->accountIds, true) - ) - ) { - return 'earned'; - } - - return 'spent'; - } - - private function getRate(TransactionCurrency $currency, Carbon $date): string - { - try { - $rate = $this->converter->getCurrencyRate($currency, $this->primary, $date); - } catch (FireflyException $e) { - app('log')->error($e->getMessage()); - $rate = '1'; - } - - return $rate; - } - public function setAccounts(Collection $accounts): void { $this->accountIds = $accounts->pluck('id')->toArray(); @@ -298,4 +190,112 @@ class AccountBalanceGrouped { $this->start = $start; } + + private function createDefaultDataEntry(array $journal): void + { + $currencyId = (int)$journal['currency_id']; + $this->data[$currencyId] ??= [ + 'currency_id' => (string)$currencyId, + 'currency_symbol' => $journal['currency_symbol'], + 'currency_code' => $journal['currency_code'], + 'currency_name' => $journal['currency_name'], + 'currency_decimal_places' => $journal['currency_decimal_places'], + // primary currency info (could be the same) + 'primary_currency_id' => (string)$this->primary->id, + 'primary_currency_code' => $this->primary->code, + 'primary_currency_symbol' => $this->primary->symbol, + 'primary_currency_decimal_places' => $this->primary->decimal_places, + ]; + } + + private function createDefaultPeriodEntry(array $journal): void + { + $currencyId = (int)$journal['currency_id']; + $period = $journal['date']->format($this->carbonFormat); + $this->data[$currencyId][$period] ??= [ + 'period' => $period, + 'spent' => '0', + 'earned' => '0', + 'pc_spent' => '0', + 'pc_earned' => '0', + ]; + } + + private function findCurrency(int $currencyId): TransactionCurrency + { + if (array_key_exists($currencyId, $this->currencies)) { + return $this->currencies[$currencyId]; + } + $this->currencies[$currencyId] = Amount::getTransactionCurrencyById($currencyId); + + return $this->currencies[$currencyId]; + } + + private function getDataKey(array $journal): string + { + // deposit = incoming + // transfer or reconcile or opening balance, and these accounts are the destination. + if ( + TransactionTypeEnum::DEPOSIT->value === $journal['transaction_type_type'] + + || ( + ( + TransactionTypeEnum::TRANSFER->value === $journal['transaction_type_type'] + || TransactionTypeEnum::RECONCILIATION->value === $journal['transaction_type_type'] + || TransactionTypeEnum::OPENING_BALANCE->value === $journal['transaction_type_type'] + ) + && in_array($journal['destination_account_id'], $this->accountIds, true) + ) + ) { + return 'earned'; + } + + return 'spent'; + } + + private function getRate(TransactionCurrency $currency, Carbon $date): string + { + try { + $rate = $this->converter->getCurrencyRate($currency, $this->primary, $date); + } catch (FireflyException $e) { + app('log')->error($e->getMessage()); + $rate = '1'; + } + + return $rate; + } + + private function processJournal(array $journal): void + { + // format the date according to the period + $period = $journal['date']->format($this->carbonFormat); + $currencyId = (int)$journal['currency_id']; + $currency = $this->findCurrency($currencyId); + + // set the array with monetary info, if it does not exist. + $this->createDefaultDataEntry($journal); + // set the array (in monetary info) with spent/earned in this $period, if it does not exist. + $this->createDefaultPeriodEntry($journal); + + // is this journal's amount in- our outgoing? + $key = $this->getDataKey($journal); + $amount = 'spent' === $key ? Steam::negative($journal['amount']) : Steam::positive($journal['amount']); + + // get conversion rate + $rate = $this->getRate($currency, $journal['date']); + $amountConverted = bcmul($amount, $rate); + + // perhaps transaction already has the foreign amount in the primary currency. + if ((int)$journal['foreign_currency_id'] === $this->primary->id) { + $amountConverted = $journal['foreign_amount'] ?? '0'; + $amountConverted = 'earned' === $key ? Steam::positive($amountConverted) : Steam::negative($amountConverted); + } + + // add normal entry + $this->data[$currencyId][$period][$key] = bcadd((string)$this->data[$currencyId][$period][$key], $amount); + + // add converted entry + $convertedKey = sprintf('pc_%s', $key); + $this->data[$currencyId][$period][$convertedKey] = bcadd((string)$this->data[$currencyId][$period][$convertedKey], $amountConverted); + } } diff --git a/app/Support/Http/Api/CleansChartData.php b/app/Support/Http/Api/CleansChartData.php index a3d54974cf..aef2ec443f 100644 --- a/app/Support/Http/Api/CleansChartData.php +++ b/app/Support/Http/Api/CleansChartData.php @@ -43,7 +43,7 @@ trait CleansChartData $return = []; /** - * @var int $index + * @var int $index * @var array $array */ foreach ($data as $index => $array) { diff --git a/app/Support/Http/Api/ExchangeRateConverter.php b/app/Support/Http/Api/ExchangeRateConverter.php index c78b7afce8..84d57a4649 100644 --- a/app/Support/Http/Api/ExchangeRateConverter.php +++ b/app/Support/Http/Api/ExchangeRateConverter.php @@ -94,57 +94,22 @@ class ExchangeRateConverter return '0' === $rate ? '1' : $rate; } - /** - * @throws FireflyException - */ - private function getRate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string + public function setIgnoreSettings(bool $ignoreSettings): void { - $key = $this->getCacheKey($from, $to, $date); - $res = Cache::get($key, null); + $this->ignoreSettings = $ignoreSettings; + } - // find in cache - if (null !== $res) { - Log::debug(sprintf('ExchangeRateConverter: Return cached rate (%s) from %s to %s on %s.', $res, $from->code, $to->code, $date->format('Y-m-d'))); + public function setUserGroup(UserGroup $userGroup): void + { + $this->userGroup = $userGroup; + } - return $res; + public function summarize(): void + { + if (false === $this->enabled()) { + return; } - - // find in database - $rate = $this->getFromDB($from->id, $to->id, $date->format('Y-m-d')); - if (null !== $rate) { - Cache::forever($key, $rate); - Log::debug(sprintf('ExchangeRateConverter: Return DB rate from %s to %s on %s.', $from->code, $to->code, $date->format('Y-m-d'))); - - return $rate; - } - - // find reverse in database - $rate = $this->getFromDB($to->id, $from->id, $date->format('Y-m-d')); - if (null !== $rate) { - $rate = bcdiv('1', $rate); - Cache::forever($key, $rate); - Log::debug(sprintf('ExchangeRateConverter: Return inverse DB rate from %s to %s on %s.', $from->code, $to->code, $date->format('Y-m-d'))); - - return $rate; - } - - // fallback scenario. - $first = $this->getEuroRate($from, $date); - $second = $this->getEuroRate($to, $date); - - // combined (if present), they can be used to calculate the necessary conversion rate. - if (0 === bccomp('0', $first) || 0 === bccomp('0', $second)) { - Log::warning(sprintf('There is not enough information to convert %s to %s on date %s', $from->code, $to->code, $date->format('Y-m-d'))); - - return '1'; - } - - $second = bcdiv('1', $second); - $rate = bcmul($first, $second); - Log::debug(sprintf('ExchangeRateConverter: Return DB rate from %s to %s on %s.', $from->code, $to->code, $date->format('Y-m-d'))); - Cache::forever($key, $rate); - - return $rate; + Log::debug(sprintf('ExchangeRateConverter ran %d queries.', $this->queryCount)); } private function getCacheKey(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string @@ -152,6 +117,57 @@ class ExchangeRateConverter return sprintf('cer-%d-%d-%s', $from->id, $to->id, $date->format('Y-m-d')); } + /** + * @throws FireflyException + */ + private function getEuroId(): int + { + Log::debug('getEuroId()'); + $cache = new CacheProperties(); + $cache->addProperty('cer-euro-id'); + if ($cache->has()) { + return (int)$cache->get(); + } + $euro = Amount::getTransactionCurrencyByCode('EUR'); + ++$this->queryCount; + $cache->store($euro->id); + + return $euro->id; + } + + /** + * @throws FireflyException + */ + private function getEuroRate(TransactionCurrency $currency, Carbon $date): string + { + $euroId = $this->getEuroId(); + if ($euroId === $currency->id) { + return '1'; + } + $rate = $this->getFromDB($currency->id, $euroId, $date->format('Y-m-d')); + + if (null !== $rate) { + // app('log')->debug(sprintf('Rate for %s to EUR is %s.', $currency->code, $rate)); + return $rate; + } + $rate = $this->getFromDB($euroId, $currency->id, $date->format('Y-m-d')); + if (null !== $rate) { + return bcdiv('1', $rate); + // app('log')->debug(sprintf('Inverted rate for %s to EUR is %s.', $currency->code, $rate)); + // return $rate; + } + // grab backup values from config file: + $backup = config(sprintf('cer.rates.%s', $currency->code)); + if (null !== $backup) { + return bcdiv('1', (string)$backup); + // app('log')->debug(sprintf('Backup rate for %s to EUR is %s.', $currency->code, $backup)); + // return $backup; + } + + // app('log')->debug(sprintf('No rate for %s to EUR.', $currency->code)); + return '0'; + } + private function getFromDB(int $from, int $to, string $date): ?string { if ($from === $to) { @@ -223,69 +239,53 @@ class ExchangeRateConverter /** * @throws FireflyException */ - private function getEuroRate(TransactionCurrency $currency, Carbon $date): string + private function getRate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string { - $euroId = $this->getEuroId(); - if ($euroId === $currency->id) { - return '1'; - } - $rate = $this->getFromDB($currency->id, $euroId, $date->format('Y-m-d')); + $key = $this->getCacheKey($from, $to, $date); + $res = Cache::get($key, null); + // find in cache + if (null !== $res) { + Log::debug(sprintf('ExchangeRateConverter: Return cached rate (%s) from %s to %s on %s.', $res, $from->code, $to->code, $date->format('Y-m-d'))); + + return $res; + } + + // find in database + $rate = $this->getFromDB($from->id, $to->id, $date->format('Y-m-d')); if (null !== $rate) { - // app('log')->debug(sprintf('Rate for %s to EUR is %s.', $currency->code, $rate)); + Cache::forever($key, $rate); + Log::debug(sprintf('ExchangeRateConverter: Return DB rate from %s to %s on %s.', $from->code, $to->code, $date->format('Y-m-d'))); + return $rate; } - $rate = $this->getFromDB($euroId, $currency->id, $date->format('Y-m-d')); + + // find reverse in database + $rate = $this->getFromDB($to->id, $from->id, $date->format('Y-m-d')); if (null !== $rate) { - return bcdiv('1', $rate); - // app('log')->debug(sprintf('Inverted rate for %s to EUR is %s.', $currency->code, $rate)); - // return $rate; - } - // grab backup values from config file: - $backup = config(sprintf('cer.rates.%s', $currency->code)); - if (null !== $backup) { - return bcdiv('1', (string)$backup); - // app('log')->debug(sprintf('Backup rate for %s to EUR is %s.', $currency->code, $backup)); - // return $backup; + $rate = bcdiv('1', $rate); + Cache::forever($key, $rate); + Log::debug(sprintf('ExchangeRateConverter: Return inverse DB rate from %s to %s on %s.', $from->code, $to->code, $date->format('Y-m-d'))); + + return $rate; } - // app('log')->debug(sprintf('No rate for %s to EUR.', $currency->code)); - return '0'; - } + // fallback scenario. + $first = $this->getEuroRate($from, $date); + $second = $this->getEuroRate($to, $date); - /** - * @throws FireflyException - */ - private function getEuroId(): int - { - Log::debug('getEuroId()'); - $cache = new CacheProperties(); - $cache->addProperty('cer-euro-id'); - if ($cache->has()) { - return (int)$cache->get(); + // combined (if present), they can be used to calculate the necessary conversion rate. + if (0 === bccomp('0', $first) || 0 === bccomp('0', $second)) { + Log::warning(sprintf('There is not enough information to convert %s to %s on date %s', $from->code, $to->code, $date->format('Y-m-d'))); + + return '1'; } - $euro = Amount::getTransactionCurrencyByCode('EUR'); - ++$this->queryCount; - $cache->store($euro->id); - return $euro->id; - } + $second = bcdiv('1', $second); + $rate = bcmul($first, $second); + Log::debug(sprintf('ExchangeRateConverter: Return DB rate from %s to %s on %s.', $from->code, $to->code, $date->format('Y-m-d'))); + Cache::forever($key, $rate); - public function setIgnoreSettings(bool $ignoreSettings): void - { - $this->ignoreSettings = $ignoreSettings; - } - - public function setUserGroup(UserGroup $userGroup): void - { - $this->userGroup = $userGroup; - } - - public function summarize(): void - { - if (false === $this->enabled()) { - return; - } - Log::debug(sprintf('ExchangeRateConverter ran %d queries.', $this->queryCount)); + return $rate; } } diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index 7371b997bd..1dda34bf8d 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -38,6 +38,7 @@ use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface; use FireflyIII\Support\CacheProperties; use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\Navigation; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; /** @@ -72,6 +73,7 @@ trait PeriodOverview protected AccountRepositoryInterface $accountRepository; protected JournalRepositoryInterface $journalRepos; protected PeriodStatisticRepositoryInterface $periodStatisticRepo; + private Collection $statistics; /** * This method returns "period entries", so nov-2015, dec-2015, etc. (this depends on the users session range) @@ -82,30 +84,231 @@ trait PeriodOverview */ protected function getAccountPeriodOverview(Account $account, Carbon $start, Carbon $end): array { - Log::debug(sprintf('Now in getAccountPeriodOverview(#%d, %s %s)', $account->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); + Log::debug(sprintf('Now in getAccountPeriodOverview(#%d, %s %s)', $account->id, $start->format('Y-m-d H:i:s.u'), $end->format('Y-m-d H:i:s.u'))); $this->accountRepository = app(AccountRepositoryInterface::class); $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + $this->statistics = $this->periodStatisticRepo->allInRangeForModel($account, $start, $end); + + // TODO needs to be re-arranged: + // get all period stats for entire range. + // loop blocks, an loop the types, and select the missing ones. + // create new ones, or use collected. + /** @var array $dates */ $dates = Navigation::blockPeriods($start, $end, $range); $entries = []; + $types = ['spent', 'earned', 'transferred_in', 'transferred_away']; Log::debug(sprintf('Count of loops: %d', count($dates))); foreach ($dates as $currentDate) { - $entries[] = $this->getSingleAccountPeriod($account, $currentDate['start'], $currentDate['end']); + $entries[] = $this->getSingleAccountPeriod($account, $currentDate['period'], $currentDate['start'], $currentDate['end']); } Log::debug('End of getAccountPeriodOverview()'); return $entries; } - protected function getSingleAccountPeriod(Account $account, Carbon $start, Carbon $end): array + /** + * Overview for single category. Has been refactored recently. + * + * @throws FireflyException + */ + protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array + { + $range = Navigation::getViewRange(true); + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + + // properties for entries with their amounts. + $cache = new CacheProperties(); + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty($range); + $cache->addProperty('category-show-period-entries'); + $cache->addProperty($category->id); + + if ($cache->has()) { + return $cache->get(); + } + + /** @var array $dates */ + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; + + // collect all expenses in this period: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setCategory($category); + $collector->setRange($start, $end); + $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); + $earnedSet = $collector->getExtractedJournals(); + + // collect all income in this period: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setCategory($category); + $collector->setRange($start, $end); + $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); + $spentSet = $collector->getExtractedJournals(); + + // collect all transfers in this period: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setCategory($category); + $collector->setRange($start, $end); + $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); + $transferSet = $collector->getExtractedJournals(); + foreach ($dates as $currentDate) { + $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); + $earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']); + $transferred = $this->filterJournalsByDate($transferSet, $currentDate['start'], $currentDate['end']); + $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); + $entries[] + = [ + 'transactions' => 0, + 'title' => $title, + 'route' => route( + 'categories.show', + [$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] + ), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; + } + $cache->store($entries); + + return $entries; + } + + /** + * Same as above, but for lists that involve transactions without a budget. + * + * This method has been refactored recently. + * + * @throws FireflyException + */ + protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array + { + $range = Navigation::getViewRange(true); + + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + + $cache = new CacheProperties(); + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty($this->convertToPrimary); + $cache->addProperty('no-budget-period-entries'); + + if ($cache->has()) { + return $cache->get(); + } + + /** @var array $dates */ + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; + + // get all expenses without a budget. + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setRange($start, $end)->withoutBudget()->withAccountInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); + $journals = $collector->getExtractedJournals(); + + foreach ($dates as $currentDate) { + $set = $this->filterJournalsByDate($journals, $currentDate['start'], $currentDate['end']); + $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); + $entries[] + = [ + 'title' => $title, + 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($set), + 'spent' => $this->groupByCurrency($set), + 'earned' => [], + 'transferred_away' => [], + 'transferred_in' => [], + ]; + } + $cache->store($entries); + + return $entries; + } + + /** + * TODO fix the date. + * + * Show period overview for no category view. + * + * @throws FireflyException + */ + protected function getNoCategoryPeriodOverview(Carbon $theDate): array + { + Log::debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d'))); + $range = Navigation::getViewRange(true); + $first = $this->journalRepos->firstNull(); + $start = null === $first ? new Carbon() : $first->date; + $end = clone $theDate; + $end = Navigation::endOfPeriod($end, $range); + + Log::debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d'))); + Log::debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d'))); + + // properties for cache + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; + + // collect all expenses in this period: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->withoutCategory(); + $collector->setRange($start, $end); + $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); + $earnedSet = $collector->getExtractedJournals(); + + // collect all income in this period: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->withoutCategory(); + $collector->setRange($start, $end); + $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); + $spentSet = $collector->getExtractedJournals(); + + // collect all transfers in this period: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->withoutCategory(); + $collector->setRange($start, $end); + $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); + $transferSet = $collector->getExtractedJournals(); + + /** @var array $currentDate */ + foreach ($dates as $currentDate) { + $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); + $earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']); + $transferred = $this->filterJournalsByDate($transferSet, $currentDate['start'], $currentDate['end']); + $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); + $entries[] + = [ + 'title' => $title, + 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; + } + Log::debug('End of loops'); + + return $entries; + } + + protected function getSingleAccountPeriod(Account $account, string $period, Carbon $start, Carbon $end): array { Log::debug(sprintf('Now in getSingleAccountPeriod(#%d, %s %s)', $account->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); $types = ['spent', 'earned', 'transferred_in', 'transferred_away']; $return = [ - 'title' => Navigation::periodShow($start, $end), + 'title' => Navigation::periodShow($start, $period), 'route' => route('accounts.show', [$account->id, $start->format('Y-m-d'), $start->format('Y-m-d')]), 'total_transactions' => 0, ]; @@ -119,13 +322,34 @@ trait PeriodOverview return $return; } + protected function filterStatistics(Carbon $start, Carbon $end, string $type): Collection + { + return $this->statistics->filter( + function (PeriodStatistic $statistic) use ($start, $end, $type) { + if( + !$statistic->end->equalTo($end) + && $statistic->end->format('Y-m-d H:i:s') === $end->format('Y-m-d H:i:s') + ) { + echo sprintf('End: "%s" vs "%s": %s', $statistic->end->toW3cString(), $end->toW3cString(), var_export($statistic->end->eq($end), true)); + var_dump($statistic->end); + var_dump($end); + exit; + } + + + return $statistic->start->eq($start) && $statistic->end->eq($end) && $statistic->type === $type; + } + ); + } + protected function getSingleAccountPeriodByType(Account $account, Carbon $start, Carbon $end, string $type): array { Log::debug(sprintf('Now in getSingleAccountPeriodByType(#%d, %s %s, %s)', $account->id, $start->format('Y-m-d'), $end->format('Y-m-d'), $type)); - $statistics = $this->periodStatisticRepo->findPeriodStatistic($account, $start, $end, $type); + $statistics = $this->filterStatistics($start, $end, $type); // nothing found, regenerate them. if (0 === $statistics->count()) { + Log::debug(sprintf('Found nothing in this period for type "%s"', $type)); $transactions = $this->accountRepository->periodCollection($account, $start, $end); switch ($type) { @@ -183,12 +407,195 @@ trait PeriodOverview return $grouped; } + /** + * This shows a period overview for a tag. It goes back in time and lists all relevant transactions and sums. + * + * @throws FireflyException + */ + protected function getTagPeriodOverview(Tag $tag, Carbon $start, Carbon $end): array // period overview for tags. + { + $range = Navigation::getViewRange(true); + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + + // properties for cache + $cache = new CacheProperties(); + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('tag-period-entries'); + $cache->addProperty($tag->id); + if ($cache->has()) { + return $cache->get(); + } + + /** @var array $dates */ + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; + + // collect all expenses in this period: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setTag($tag); + $collector->setRange($start, $end); + $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); + $earnedSet = $collector->getExtractedJournals(); + + // collect all income in this period: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setTag($tag); + $collector->setRange($start, $end); + $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); + $spentSet = $collector->getExtractedJournals(); + + // collect all transfers in this period: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setTag($tag); + $collector->setRange($start, $end); + $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); + $transferSet = $collector->getExtractedJournals(); + + // filer all of them: + $earnedSet = $this->filterJournalsByTag($earnedSet, $tag); + $spentSet = $this->filterJournalsByTag($spentSet, $tag); + $transferSet = $this->filterJournalsByTag($transferSet, $tag); + + foreach ($dates as $currentDate) { + $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); + $earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']); + $transferred = $this->filterJournalsByDate($transferSet, $currentDate['start'], $currentDate['end']); + $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); + $entries[] + = [ + 'transactions' => 0, + 'title' => $title, + 'route' => route( + 'tags.show', + [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] + ), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; + } + + return $entries; + } + + /** + * @throws FireflyException + */ + protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array + { + $range = Navigation::getViewRange(true); + $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + + // properties for cache + $cache = new CacheProperties(); + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('transactions-period-entries'); + $cache->addProperty($transactionType); + if ($cache->has()) { + return $cache->get(); + } + + /** @var array $dates */ + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; + $spent = []; + $earned = []; + $transferred = []; + // collect all journals in this period (regardless of type) + $collector = app(GroupCollectorInterface::class); + $collector->setTypes($types)->setRange($start, $end); + $genericSet = $collector->getExtractedJournals(); + $loops = 0; + + foreach ($dates as $currentDate) { + $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); + + if ($loops < 10) { + // set to correct array + if ('expenses' === $transactionType || 'withdrawal' === $transactionType) { + $spent = $this->filterJournalsByDate($genericSet, $currentDate['start'], $currentDate['end']); + } + if ('revenue' === $transactionType || 'deposit' === $transactionType) { + $earned = $this->filterJournalsByDate($genericSet, $currentDate['start'], $currentDate['end']); + } + if ('transfer' === $transactionType || 'transfers' === $transactionType) { + $transferred = $this->filterJournalsByDate($genericSet, $currentDate['start'], $currentDate['end']); + } + } + $entries[] + = [ + 'title' => $title, + 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; + ++$loops; + } + + return $entries; + } + + protected function saveGroupedAsStatistics(Account $account, Carbon $start, Carbon $end, string $type, array $array): void + { + unset($array['count']); + foreach ($array as $entry) { + $this->periodStatisticRepo->saveStatistic($account, $entry['currency_id'], $start, $end, $type, $entry['count'], $entry['amount']); + } + } + + /** + * Filter a list of journals by a set of dates, and then group them by currency. + */ + private function filterJournalsByDate(array $array, Carbon $start, Carbon $end): array + { + $result = []; + + /** @var array $journal */ + foreach ($array as $journal) { + if ($journal['date'] <= $end && $journal['date'] >= $start) { + $result[] = $journal; + } + } + + return $result; + } + + private function filterJournalsByTag(array $set, Tag $tag): array + { + $return = []; + foreach ($set as $entry) { + $found = false; + + /** @var array $localTag */ + foreach ($entry['tags'] as $localTag) { + if ($localTag['id'] === $tag->id) { + $found = true; + } + } + if (false === $found) { + continue; + } + $return[] = $entry; + } + + return $return; + } + private function filterTransactionsByType(TransactionTypeEnum $type, array $transactions, Carbon $start, Carbon $end): array { $result = []; /** - * @var int $index + * @var int $index * @var array $item */ foreach ($transactions as $index => $item) { @@ -203,12 +610,46 @@ trait PeriodOverview return $result; } + /** + * Return only transactions where $account is the source. + */ + private function filterTransferredAway(Account $account, array $journals): array + { + $return = []; + + /** @var array $journal */ + foreach ($journals as $journal) { + if ($account->id === (int)$journal['source_account_id']) { + $return[] = $journal; + } + } + + return $return; + } + + /** + * Return only transactions where $account is the source. + */ + private function filterTransferredIn(Account $account, array $journals): array + { + $return = []; + + /** @var array $journal */ + foreach ($journals as $journal) { + if ($account->id === (int)$journal['destination_account_id']) { + $return[] = $journal; + } + } + + return $return; + } + private function filterTransfers(string $direction, array $transactions, Carbon $start, Carbon $end): array { $result = []; /** - * @var int $index + * @var int $index * @var array $item */ foreach ($transactions as $index => $item) { @@ -289,414 +730,4 @@ trait PeriodOverview return $return; } - - protected function saveGroupedAsStatistics(Account $account, Carbon $start, Carbon $end, string $type, array $array): void - { - unset($array['count']); - foreach ($array as $entry) { - $this->periodStatisticRepo->saveStatistic($account, $entry['currency_id'], $start, $end, $type, $entry['count'], $entry['amount']); - } - } - - /** - * Overview for single category. Has been refactored recently. - * - * @throws FireflyException - */ - protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array - { - $range = Navigation::getViewRange(true); - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; - - // properties for entries with their amounts. - $cache = new CacheProperties(); - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty($range); - $cache->addProperty('category-show-period-entries'); - $cache->addProperty($category->id); - - if ($cache->has()) { - return $cache->get(); - } - - /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - - // collect all expenses in this period: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->setCategory($category); - $collector->setRange($start, $end); - $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); - - // collect all income in this period: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->setCategory($category); - $collector->setRange($start, $end); - $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); - - // collect all transfers in this period: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->setCategory($category); - $collector->setRange($start, $end); - $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); - $transferSet = $collector->getExtractedJournals(); - foreach ($dates as $currentDate) { - $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); - $earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']); - $transferred = $this->filterJournalsByDate($transferSet, $currentDate['start'], $currentDate['end']); - $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); - $entries[] - = [ - 'transactions' => 0, - 'title' => $title, - 'route' => route( - 'categories.show', - [$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] - ), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; - } - $cache->store($entries); - - return $entries; - } - - /** - * Filter a list of journals by a set of dates, and then group them by currency. - */ - private function filterJournalsByDate(array $array, Carbon $start, Carbon $end): array - { - $result = []; - - /** @var array $journal */ - foreach ($array as $journal) { - if ($journal['date'] <= $end && $journal['date'] >= $start) { - $result[] = $journal; - } - } - - return $result; - } - - /** - * Same as above, but for lists that involve transactions without a budget. - * - * This method has been refactored recently. - * - * @throws FireflyException - */ - protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array - { - $range = Navigation::getViewRange(true); - - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; - - $cache = new CacheProperties(); - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty($this->convertToPrimary); - $cache->addProperty('no-budget-period-entries'); - - if ($cache->has()) { - return $cache->get(); - } - - /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - - // get all expenses without a budget. - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->setRange($start, $end)->withoutBudget()->withAccountInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $journals = $collector->getExtractedJournals(); - - foreach ($dates as $currentDate) { - $set = $this->filterJournalsByDate($journals, $currentDate['start'], $currentDate['end']); - $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); - $entries[] - = [ - 'title' => $title, - 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($set), - 'spent' => $this->groupByCurrency($set), - 'earned' => [], - 'transferred_away' => [], - 'transferred_in' => [], - ]; - } - $cache->store($entries); - - return $entries; - } - - /** - * TODO fix the date. - * - * Show period overview for no category view. - * - * @throws FireflyException - */ - protected function getNoCategoryPeriodOverview(Carbon $theDate): array - { - app('log')->debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d'))); - $range = Navigation::getViewRange(true); - $first = $this->journalRepos->firstNull(); - $start = null === $first ? new Carbon() : $first->date; - $end = clone $theDate; - $end = Navigation::endOfPeriod($end, $range); - - app('log')->debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d'))); - app('log')->debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d'))); - - // properties for cache - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - - // collect all expenses in this period: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->withoutCategory(); - $collector->setRange($start, $end); - $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); - - // collect all income in this period: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->withoutCategory(); - $collector->setRange($start, $end); - $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); - - // collect all transfers in this period: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->withoutCategory(); - $collector->setRange($start, $end); - $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); - $transferSet = $collector->getExtractedJournals(); - - /** @var array $currentDate */ - foreach ($dates as $currentDate) { - $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); - $earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']); - $transferred = $this->filterJournalsByDate($transferSet, $currentDate['start'], $currentDate['end']); - $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); - $entries[] - = [ - 'title' => $title, - 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; - } - app('log')->debug('End of loops'); - - return $entries; - } - - /** - * This shows a period overview for a tag. It goes back in time and lists all relevant transactions and sums. - * - * @throws FireflyException - */ - protected function getTagPeriodOverview(Tag $tag, Carbon $start, Carbon $end): array // period overview for tags. - { - $range = Navigation::getViewRange(true); - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; - - // properties for cache - $cache = new CacheProperties(); - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty('tag-period-entries'); - $cache->addProperty($tag->id); - if ($cache->has()) { - return $cache->get(); - } - - /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - - // collect all expenses in this period: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->setTag($tag); - $collector->setRange($start, $end); - $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); - - // collect all income in this period: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->setTag($tag); - $collector->setRange($start, $end); - $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); - - // collect all transfers in this period: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->setTag($tag); - $collector->setRange($start, $end); - $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); - $transferSet = $collector->getExtractedJournals(); - - // filer all of them: - $earnedSet = $this->filterJournalsByTag($earnedSet, $tag); - $spentSet = $this->filterJournalsByTag($spentSet, $tag); - $transferSet = $this->filterJournalsByTag($transferSet, $tag); - - foreach ($dates as $currentDate) { - $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); - $earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']); - $transferred = $this->filterJournalsByDate($transferSet, $currentDate['start'], $currentDate['end']); - $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); - $entries[] - = [ - 'transactions' => 0, - 'title' => $title, - 'route' => route( - 'tags.show', - [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] - ), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; - } - - return $entries; - } - - private function filterJournalsByTag(array $set, Tag $tag): array - { - $return = []; - foreach ($set as $entry) { - $found = false; - - /** @var array $localTag */ - foreach ($entry['tags'] as $localTag) { - if ($localTag['id'] === $tag->id) { - $found = true; - } - } - if (false === $found) { - continue; - } - $return[] = $entry; - } - - return $return; - } - - /** - * @throws FireflyException - */ - protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array - { - $range = Navigation::getViewRange(true); - $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; - - // properties for cache - $cache = new CacheProperties(); - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty('transactions-period-entries'); - $cache->addProperty($transactionType); - if ($cache->has()) { - return $cache->get(); - } - - /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - $spent = []; - $earned = []; - $transferred = []; - // collect all journals in this period (regardless of type) - $collector = app(GroupCollectorInterface::class); - $collector->setTypes($types)->setRange($start, $end); - $genericSet = $collector->getExtractedJournals(); - $loops = 0; - - foreach ($dates as $currentDate) { - $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); - - if ($loops < 10) { - // set to correct array - if ('expenses' === $transactionType || 'withdrawal' === $transactionType) { - $spent = $this->filterJournalsByDate($genericSet, $currentDate['start'], $currentDate['end']); - } - if ('revenue' === $transactionType || 'deposit' === $transactionType) { - $earned = $this->filterJournalsByDate($genericSet, $currentDate['start'], $currentDate['end']); - } - if ('transfer' === $transactionType || 'transfers' === $transactionType) { - $transferred = $this->filterJournalsByDate($genericSet, $currentDate['start'], $currentDate['end']); - } - } - $entries[] - = [ - 'title' => $title, - 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; - ++$loops; - } - - return $entries; - } - - /** - * Return only transactions where $account is the source. - */ - private function filterTransferredAway(Account $account, array $journals): array - { - $return = []; - - /** @var array $journal */ - foreach ($journals as $journal) { - if ($account->id === (int)$journal['source_account_id']) { - $return[] = $journal; - } - } - - return $return; - } - - /** - * Return only transactions where $account is the source. - */ - private function filterTransferredIn(Account $account, array $journals): array - { - $return = []; - - /** @var array $journal */ - foreach ($journals as $journal) { - if ($account->id === (int)$journal['destination_account_id']) { - $return[] = $journal; - } - } - - return $return; - } } diff --git a/app/Support/Http/Controllers/RequestInformation.php b/app/Support/Http/Controllers/RequestInformation.php index 73a39655e6..b7600f778a 100644 --- a/app/Support/Http/Controllers/RequestInformation.php +++ b/app/Support/Http/Controllers/RequestInformation.php @@ -53,6 +53,22 @@ trait RequestInformation return $parts['host'] ?? ''; } + final protected function getPageName(): string // get request info + { + return str_replace('.', '_', RouteFacade::currentRouteName()); + } + + /** + * Get the specific name of a page for intro. + */ + final protected function getSpecificPageName(): string // get request info + { + /** @var null|string $param */ + $param = RouteFacade::current()->parameter('objectType'); + + return null === $param ? '' : sprintf('_%s', $param); + } + /** * Get a list of triggers. */ @@ -102,22 +118,6 @@ trait RequestInformation return $shownDemo; } - final protected function getPageName(): string // get request info - { - return str_replace('.', '_', RouteFacade::currentRouteName()); - } - - /** - * Get the specific name of a page for intro. - */ - final protected function getSpecificPageName(): string // get request info - { - /** @var null|string $param */ - $param = RouteFacade::current()->parameter('objectType'); - - return null === $param ? '' : sprintf('_%s', $param); - } - /** * Check if date is outside session range. */ diff --git a/app/Support/JsonApi/Enrichments/AccountEnrichment.php b/app/Support/JsonApi/Enrichments/AccountEnrichment.php index 62dee47082..7dc3cb444c 100644 --- a/app/Support/JsonApi/Enrichments/AccountEnrichment.php +++ b/app/Support/JsonApi/Enrichments/AccountEnrichment.php @@ -53,29 +53,29 @@ use Override; */ class AccountEnrichment implements EnrichmentInterface { - private array $ids = []; - private array $accountTypeIds = []; - private array $accountTypes = []; - private Collection $collection; - private array $currencies = []; - private array $locations = []; - private array $meta = []; - private readonly TransactionCurrency $primaryCurrency; - private array $notes = []; - private array $openingBalances = []; - private User $user; - private UserGroup $userGroup; - private array $lastActivities = []; - private ?Carbon $date = null; - private ?Carbon $start = null; - private ?Carbon $end = null; + private array $accountTypeIds = []; + private array $accountTypes = []; + private array $balances = []; + private Collection $collection; private readonly bool $convertToPrimary; - private array $balances = []; - private array $startBalances = []; - private array $endBalances = []; - private array $objectGroups = []; - private array $mappedObjects = []; - private array $sort = []; + private array $currencies = []; + private ?Carbon $date = null; + private ?Carbon $end = null; + private array $endBalances = []; + private array $ids = []; + private array $lastActivities = []; + private array $locations = []; + private array $mappedObjects = []; + private array $meta = []; + private array $notes = []; + private array $objectGroups = []; + private array $openingBalances = []; + private readonly TransactionCurrency $primaryCurrency; + private array $sort = []; + private ?Carbon $start = null; + private array $startBalances = []; + private User $user; + private UserGroup $userGroup; /** * TODO The account enricher must do conversion from and to the primary currency. @@ -86,16 +86,6 @@ class AccountEnrichment implements EnrichmentInterface $this->convertToPrimary = Amount::convertToPrimary(); } - #[Override] - public function enrichSingle(array|Model $model): Account|array - { - Log::debug(__METHOD__); - $collection = new Collection()->push($model); - $collection = $this->enrich($collection); - - return $collection->first(); - } - #[Override] /** * Do the actual enrichment. @@ -121,114 +111,47 @@ class AccountEnrichment implements EnrichmentInterface return $this->collection; } - private function collectIds(): void + #[Override] + public function enrichSingle(array | Model $model): Account | array { - /** @var Account $account */ - foreach ($this->collection as $account) { - $this->ids[] = (int)$account->id; - $this->accountTypeIds[] = (int)$account->account_type_id; - } - $this->ids = array_unique($this->ids); - $this->accountTypeIds = array_unique($this->accountTypeIds); + Log::debug(__METHOD__); + $collection = new Collection()->push($model); + $collection = $this->enrich($collection); + + return $collection->first(); } - private function getAccountTypes(): void + public function getDate(): Carbon { - $types = AccountType::whereIn('id', $this->accountTypeIds)->get(); - - /** @var AccountType $type */ - foreach ($types as $type) { - $this->accountTypes[(int)$type->id] = $type->type; + if (!$this->date instanceof Carbon) { + return now(); } + + return $this->date; } - private function collectMetaData(): void + public function setDate(?Carbon $date): void { - $set = AccountMeta::whereIn('name', ['is_multi_currency', 'include_net_worth', 'currency_id', 'account_role', 'account_number', 'BIC', 'liability_direction', 'interest', 'interest_period', 'current_debt']) - ->whereIn('account_id', $this->ids) - ->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data'])->toArray() - ; - - /** @var array $entry */ - foreach ($set as $entry) { - $this->meta[(int)$entry['account_id']][$entry['name']] = (string)$entry['data']; - if ('currency_id' === $entry['name']) { - $this->currencies[(int)$entry['data']] = true; - } - } - if (count($this->currencies) > 0) { - $currencies = TransactionCurrency::whereIn('id', array_keys($this->currencies))->get(); - foreach ($currencies as $currency) { - $this->currencies[(int)$currency->id] = $currency; - } - } - $this->currencies[0] = $this->primaryCurrency; - foreach ($this->currencies as $id => $currency) { - if (true === $currency) { - throw new FireflyException(sprintf('Currency #%d not found.', $id)); - } + if ($date instanceof Carbon) { + $date->endOfDay(); + Log::debug(sprintf('Date is now %s', $date->toW3cString())); } + $this->date = $date; } - private function collectNotes(): void + public function setEnd(?Carbon $end): void { - $notes = Note::query()->whereIn('noteable_id', $this->ids) - ->whereNotNull('notes.text') - ->where('notes.text', '!=', '') - ->where('noteable_type', Account::class)->get(['notes.noteable_id', 'notes.text'])->toArray() - ; - foreach ($notes as $note) { - $this->notes[(int)$note['noteable_id']] = (string)$note['text']; - } - Log::debug(sprintf('Enrich with %d note(s)', count($this->notes))); + $this->end = $end; } - private function collectLocations(): void + public function setSort(array $sort): void { - $locations = Location::query()->whereIn('locatable_id', $this->ids) - ->where('locatable_type', Account::class)->get(['locations.locatable_id', 'locations.latitude', 'locations.longitude', 'locations.zoom_level'])->toArray() - ; - foreach ($locations as $location) { - $this->locations[(int)$location['locatable_id']] - = [ - 'latitude' => (float)$location['latitude'], - 'longitude' => (float)$location['longitude'], - 'zoom_level' => (int)$location['zoom_level'], - ]; - } - Log::debug(sprintf('Enrich with %d locations(s)', count($this->locations))); + $this->sort = $sort; } - private function collectOpeningBalances(): void + public function setStart(?Carbon $start): void { - // use new group collector: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector - ->setUser($this->user) - ->setUserGroup($this->userGroup) - ->setAccounts($this->collection) - ->withAccountInformation() - ->setTypes([TransactionTypeEnum::OPENING_BALANCE->value]) - ; - $journals = $collector->getExtractedJournals(); - foreach ($journals as $journal) { - $this->openingBalances[(int)$journal['source_account_id']] - = [ - 'amount' => Steam::negative($journal['amount']), - 'date' => $journal['date'], - ]; - $this->openingBalances[(int)$journal['destination_account_id']] - = [ - 'amount' => Steam::positive($journal['amount']), - 'date' => $journal['date'], - ]; - } - } - - public function setUserGroup(UserGroup $userGroup): void - { - $this->userGroup = $userGroup; + $this->start = $start; } public function setUser(User $user): void @@ -237,12 +160,17 @@ class AccountEnrichment implements EnrichmentInterface $this->userGroup = $user->userGroup; } + public function setUserGroup(UserGroup $userGroup): void + { + $this->userGroup = $userGroup; + } + private function appendCollectedData(): void { $this->collection = $this->collection->map(function (Account $item) { - $id = (int)$item->id; - $item->full_account_type = $this->accountTypes[(int)$item->account_type_id] ?? null; - $meta = [ + $id = (int)$item->id; + $item->full_account_type = $this->accountTypes[(int)$item->account_type_id] ?? null; + $meta = [ 'currency' => null, 'location' => [ 'latitude' => null, @@ -289,30 +217,30 @@ class AccountEnrichment implements EnrichmentInterface // add balances // get currencies: - $currency = $this->primaryCurrency; // assume primary currency + $currency = $this->primaryCurrency; // assume primary currency if (null !== $meta['currency']) { $currency = $meta['currency']; } // get the current balance: - $date = $this->getDate(); + $date = $this->getDate(); // $finalBalance = Steam::finalAccountBalance($item, $date, $this->primaryCurrency, $this->convertToPrimary); - $finalBalance = $this->balances[$id]; - $balanceDifference = $this->getBalanceDifference($id, $currency); + $finalBalance = $this->balances[$id]; + $balanceDifference = $this->getBalanceDifference($id, $currency); Log::debug(sprintf('Call finalAccountBalance(%s) with date/time "%s"', var_export($this->convertToPrimary, true), $date->toIso8601String()), $finalBalance); // collect current balances: - $currentBalance = Steam::bcround($finalBalance[$currency->code] ?? '0', $currency->decimal_places); - $openingBalance = Steam::bcround($meta['opening_balance_amount'] ?? '0', $currency->decimal_places); - $virtualBalance = Steam::bcround($item->virtual_balance ?? '0', $currency->decimal_places); - $debtAmount = $meta['current_debt'] ?? null; + $currentBalance = Steam::bcround($finalBalance[$currency->code] ?? '0', $currency->decimal_places); + $openingBalance = Steam::bcround($meta['opening_balance_amount'] ?? '0', $currency->decimal_places); + $virtualBalance = Steam::bcround($item->virtual_balance ?? '0', $currency->decimal_places); + $debtAmount = $meta['current_debt'] ?? null; // set some pc_ default values to NULL: - $pcCurrentBalance = null; - $pcOpeningBalance = null; - $pcVirtualBalance = null; - $pcDebtAmount = null; - $pcBalanceDifference = null; + $pcCurrentBalance = null; + $pcOpeningBalance = null; + $pcVirtualBalance = null; + $pcDebtAmount = null; + $pcBalanceDifference = null; // convert to primary currency if needed: if ($this->convertToPrimary && $currency->id !== $this->primaryCurrency->id) { @@ -351,17 +279,12 @@ class AccountEnrichment implements EnrichmentInterface 'pc_balance_difference' => $pcBalanceDifference, ]; // end add balances - $item->meta = $meta; + $item->meta = $meta; return $item; }); } - private function collectLastActivities(): void - { - $this->lastActivities = Steam::getLastActivities($this->ids); - } - private function collectBalances(): void { $this->balances = Steam::accountsBalancesOptimized($this->collection, $this->getDate(), $this->primaryCurrency, $this->convertToPrimary); @@ -371,15 +294,84 @@ class AccountEnrichment implements EnrichmentInterface } } + private function collectIds(): void + { + /** @var Account $account */ + foreach ($this->collection as $account) { + $this->ids[] = (int)$account->id; + $this->accountTypeIds[] = (int)$account->account_type_id; + } + $this->ids = array_unique($this->ids); + $this->accountTypeIds = array_unique($this->accountTypeIds); + } + + private function collectLastActivities(): void + { + $this->lastActivities = Steam::getLastActivities($this->ids); + } + + private function collectLocations(): void + { + $locations = Location::query()->whereIn('locatable_id', $this->ids) + ->where('locatable_type', Account::class)->get(['locations.locatable_id', 'locations.latitude', 'locations.longitude', 'locations.zoom_level'])->toArray(); + foreach ($locations as $location) { + $this->locations[(int)$location['locatable_id']] + = [ + 'latitude' => (float)$location['latitude'], + 'longitude' => (float)$location['longitude'], + 'zoom_level' => (int)$location['zoom_level'], + ]; + } + Log::debug(sprintf('Enrich with %d locations(s)', count($this->locations))); + } + + private function collectMetaData(): void + { + $set = AccountMeta::whereIn('name', ['is_multi_currency', 'include_net_worth', 'currency_id', 'account_role', 'account_number', 'BIC', 'liability_direction', 'interest', 'interest_period', 'current_debt']) + ->whereIn('account_id', $this->ids) + ->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data'])->toArray(); + + /** @var array $entry */ + foreach ($set as $entry) { + $this->meta[(int)$entry['account_id']][$entry['name']] = (string)$entry['data']; + if ('currency_id' === $entry['name']) { + $this->currencies[(int)$entry['data']] = true; + } + } + if (count($this->currencies) > 0) { + $currencies = TransactionCurrency::whereIn('id', array_keys($this->currencies))->get(); + foreach ($currencies as $currency) { + $this->currencies[(int)$currency->id] = $currency; + } + } + $this->currencies[0] = $this->primaryCurrency; + foreach ($this->currencies as $id => $currency) { + if (true === $currency) { + throw new FireflyException(sprintf('Currency #%d not found.', $id)); + } + } + } + + private function collectNotes(): void + { + $notes = Note::query()->whereIn('noteable_id', $this->ids) + ->whereNotNull('notes.text') + ->where('notes.text', '!=', '') + ->where('noteable_type', Account::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); + foreach ($notes as $note) { + $this->notes[(int)$note['noteable_id']] = (string)$note['text']; + } + Log::debug(sprintf('Enrich with %d note(s)', count($this->notes))); + } + private function collectObjectGroups(): void { - $set = DB::table('object_groupables') - ->whereIn('object_groupable_id', $this->ids) - ->where('object_groupable_type', Account::class) - ->get(['object_groupable_id', 'object_group_id']) - ; + $set = DB::table('object_groupables') + ->whereIn('object_groupable_id', $this->ids) + ->where('object_groupable_type', Account::class) + ->get(['object_groupable_id', 'object_group_id']); - $ids = array_unique($set->pluck('object_group_id')->toArray()); + $ids = array_unique($set->pluck('object_group_id')->toArray()); foreach ($set as $entry) { $this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id; @@ -393,32 +385,40 @@ class AccountEnrichment implements EnrichmentInterface } } - public function setDate(?Carbon $date): void + private function collectOpeningBalances(): void { - if ($date instanceof Carbon) { - $date->endOfDay(); - Log::debug(sprintf('Date is now %s', $date->toW3cString())); + // use new group collector: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector + ->setUser($this->user) + ->setUserGroup($this->userGroup) + ->setAccounts($this->collection) + ->withAccountInformation() + ->setTypes([TransactionTypeEnum::OPENING_BALANCE->value]); + $journals = $collector->getExtractedJournals(); + foreach ($journals as $journal) { + $this->openingBalances[(int)$journal['source_account_id']] + = [ + 'amount' => Steam::negative($journal['amount']), + 'date' => $journal['date'], + ]; + $this->openingBalances[(int)$journal['destination_account_id']] + = [ + 'amount' => Steam::positive($journal['amount']), + 'date' => $journal['date'], + ]; } - $this->date = $date; } - public function getDate(): Carbon + private function getAccountTypes(): void { - if (!$this->date instanceof Carbon) { - return now(); + $types = AccountType::whereIn('id', $this->accountTypeIds)->get(); + + /** @var AccountType $type */ + foreach ($types as $type) { + $this->accountTypes[(int)$type->id] = $type->type; } - - return $this->date; - } - - public function setStart(?Carbon $start): void - { - $this->start = $start; - } - - public function setEnd(?Carbon $end): void - { - $this->end = $end; } private function getBalanceDifference(int $id, TransactionCurrency $currency): ?string @@ -431,17 +431,12 @@ class AccountEnrichment implements EnrichmentInterface if (0 === count($startBalance) || 0 === count($endBalance)) { return null; } - $start = $startBalance[$currency->code] ?? '0'; - $end = $endBalance[$currency->code] ?? '0'; + $start = $startBalance[$currency->code] ?? '0'; + $end = $endBalance[$currency->code] ?? '0'; return bcsub($end, $start); } - public function setSort(array $sort): void - { - $this->sort = $sort; - } - private function sortData(): void { $dbParams = config('firefly.allowed_db_sort_parameters.Account', []); @@ -458,7 +453,7 @@ class AccountEnrichment implements EnrichmentInterface case 'current_balance': case 'pc_current_balance': - $this->collection = $this->collection->sortBy(static fn (Account $account) => $account->meta['balances'][$parameter[0]] ?? '0', SORT_NUMERIC, 'desc' === $parameter[1]); + $this->collection = $this->collection->sortBy(static fn(Account $account) => $account->meta['balances'][$parameter[0]] ?? '0', SORT_NUMERIC, 'desc' === $parameter[1]); break; } diff --git a/app/Support/JsonApi/Enrichments/AvailableBudgetEnrichment.php b/app/Support/JsonApi/Enrichments/AvailableBudgetEnrichment.php index 6154c941aa..85711c7efb 100644 --- a/app/Support/JsonApi/Enrichments/AvailableBudgetEnrichment.php +++ b/app/Support/JsonApi/Enrichments/AvailableBudgetEnrichment.php @@ -40,20 +40,20 @@ use Override; class AvailableBudgetEnrichment implements EnrichmentInterface { - private User $user; // @phpstan-ignore-line - private UserGroup $userGroup; // @phpstan-ignore-line - private readonly bool $convertToPrimary; - private array $ids = []; - private array $currencyIds = []; + private Collection $collection; // @phpstan-ignore-line + private readonly bool $convertToPrimary; // @phpstan-ignore-line private array $currencies = []; - private Collection $collection; - private array $spentInBudgets = []; - private array $spentOutsideBudgets = []; - private array $pcSpentInBudgets = []; - private array $pcSpentOutsideBudgets = []; + private array $currencyIds = []; + private array $ids = []; private readonly NoBudgetRepositoryInterface $noBudgetRepository; private readonly OperationsRepositoryInterface $opsRepository; + private array $pcSpentInBudgets = []; + private array $pcSpentOutsideBudgets = []; private readonly BudgetRepositoryInterface $repository; + private array $spentInBudgets = []; + private array $spentOutsideBudgets = []; + private User $user; + private UserGroup $userGroup; public function __construct() { @@ -79,7 +79,7 @@ class AvailableBudgetEnrichment implements EnrichmentInterface } #[Override] - public function enrichSingle(array|Model $model): array|Model + public function enrichSingle(array | Model $model): array | Model { Log::debug(__METHOD__); $collection = new Collection()->push($model); @@ -104,6 +104,34 @@ class AvailableBudgetEnrichment implements EnrichmentInterface $this->repository->setUserGroup($userGroup); } + private function appendCollectedData(): void + { + $this->collection = $this->collection->map(function (AvailableBudget $item) { + $id = (int)$item->id; + $currencyId = $this->currencyIds[$id]; + $currency = $this->currencies[$currencyId]; + $meta = [ + 'currency' => $currency, + 'spent_in_budgets' => $this->spentInBudgets[$id] ?? [], + 'pc_spent_in_budgets' => $this->pcSpentInBudgets[$id] ?? [], + 'spent_outside_budgets' => $this->spentOutsideBudgets[$id] ?? [], + 'pc_spent_outside_budgets' => $this->pcSpentOutsideBudgets[$id] ?? [], + ]; + $item->meta = $meta; + + return $item; + }); + } + + private function collectCurrencies(): void + { + $ids = array_unique(array_values($this->currencyIds)); + $set = TransactionCurrency::whereIn('id', $ids)->get(); + foreach ($set as $currency) { + $this->currencies[(int)$currency->id] = $currency; + } + } + private function collectIds(): void { /** @var AvailableBudget $availableBudget */ @@ -138,32 +166,4 @@ class AvailableBudgetEnrichment implements EnrichmentInterface } } } - - private function appendCollectedData(): void - { - $this->collection = $this->collection->map(function (AvailableBudget $item) { - $id = (int)$item->id; - $currencyId = $this->currencyIds[$id]; - $currency = $this->currencies[$currencyId]; - $meta = [ - 'currency' => $currency, - 'spent_in_budgets' => $this->spentInBudgets[$id] ?? [], - 'pc_spent_in_budgets' => $this->pcSpentInBudgets[$id] ?? [], - 'spent_outside_budgets' => $this->spentOutsideBudgets[$id] ?? [], - 'pc_spent_outside_budgets' => $this->pcSpentOutsideBudgets[$id] ?? [], - ]; - $item->meta = $meta; - - return $item; - }); - } - - private function collectCurrencies(): void - { - $ids = array_unique(array_values($this->currencyIds)); - $set = TransactionCurrency::whereIn('id', $ids)->get(); - foreach ($set as $currency) { - $this->currencies[(int)$currency->id] = $currency; - } - } } diff --git a/app/Support/JsonApi/Enrichments/BudgetEnrichment.php b/app/Support/JsonApi/Enrichments/BudgetEnrichment.php index 2aa21bc9cf..71e4ff160b 100644 --- a/app/Support/JsonApi/Enrichments/BudgetEnrichment.php +++ b/app/Support/JsonApi/Enrichments/BudgetEnrichment.php @@ -40,19 +40,19 @@ use Illuminate\Support\Facades\Log; class BudgetEnrichment implements EnrichmentInterface { - private Collection $collection; - private User $user; - private UserGroup $userGroup; - private array $ids = []; - private array $notes = []; - private array $autoBudgets = []; - private array $currencies = []; - private ?Carbon $start = null; - private ?Carbon $end = null; - private array $spent = []; - private array $pcSpent = []; - private array $objectGroups = []; - private array $mappedObjects = []; + private array $autoBudgets = []; + private Collection $collection; + private array $currencies = []; + private ?Carbon $end = null; + private array $ids = []; + private array $mappedObjects = []; + private array $notes = []; + private array $objectGroups = []; + private array $pcSpent = []; + private array $spent = []; + private ?Carbon $start = null; + private User $user; + private UserGroup $userGroup; public function __construct() {} @@ -70,7 +70,7 @@ class BudgetEnrichment implements EnrichmentInterface return $this->collection; } - public function enrichSingle(array|Model $model): array|Model + public function enrichSingle(array | Model $model): array | Model { Log::debug(__METHOD__); $collection = new Collection()->push($model); @@ -79,6 +79,16 @@ class BudgetEnrichment implements EnrichmentInterface return $collection->first(); } + public function setEnd(?Carbon $end): void + { + $this->end = $end; + } + + public function setStart(?Carbon $start): void + { + $this->start = $start; + } + public function setUser(User $user): void { $this->user = $user; @@ -90,33 +100,11 @@ class BudgetEnrichment implements EnrichmentInterface $this->userGroup = $userGroup; } - private function collectIds(): void - { - /** @var Budget $budget */ - foreach ($this->collection as $budget) { - $this->ids[] = (int)$budget->id; - } - $this->ids = array_unique($this->ids); - } - - private function collectNotes(): void - { - $notes = Note::query()->whereIn('noteable_id', $this->ids) - ->whereNotNull('notes.text') - ->where('notes.text', '!=', '') - ->where('noteable_type', Budget::class)->get(['notes.noteable_id', 'notes.text'])->toArray() - ; - foreach ($notes as $note) { - $this->notes[(int)$note['noteable_id']] = (string)$note['text']; - } - Log::debug(sprintf('Enrich with %d note(s)', count($this->notes))); - } - private function appendCollectedData(): void { $this->collection = $this->collection->map(function (Budget $item) { - $id = (int)$item->id; - $meta = [ + $id = (int)$item->id; + $meta = [ 'object_group_id' => null, 'object_group_order' => null, 'object_group_title' => null, @@ -130,7 +118,7 @@ class BudgetEnrichment implements EnrichmentInterface // add object group if available if (array_key_exists($id, $this->mappedObjects)) { $key = $this->mappedObjects[$id]; - $meta['object_group_id'] = (string) $this->objectGroups[$key]['id']; + $meta['object_group_id'] = (string)$this->objectGroups[$key]['id']; $meta['object_group_title'] = $this->objectGroups[$key]['title']; $meta['object_group_order'] = $this->objectGroups[$key]['order']; } @@ -168,7 +156,7 @@ class BudgetEnrichment implements EnrichmentInterface $opsRepository->setUserGroup($this->userGroup); // $spent = $this->beautify(); // $set = $this->opsRepository->sumExpenses($start, $end, null, new Collection()->push($budget)) - $expenses = $opsRepository->collectExpenses($this->start, $this->end, null, $this->collection, null); + $expenses = $opsRepository->collectExpenses($this->start, $this->end, null, $this->collection, null); foreach ($this->collection as $item) { $id = (int)$item->id; $this->spent[$id] = array_values($opsRepository->sumCollectedExpensesByBudget($expenses, $item, false)); @@ -177,25 +165,35 @@ class BudgetEnrichment implements EnrichmentInterface } } - public function setEnd(?Carbon $end): void + private function collectIds(): void { - $this->end = $end; + /** @var Budget $budget */ + foreach ($this->collection as $budget) { + $this->ids[] = (int)$budget->id; + } + $this->ids = array_unique($this->ids); } - public function setStart(?Carbon $start): void + private function collectNotes(): void { - $this->start = $start; + $notes = Note::query()->whereIn('noteable_id', $this->ids) + ->whereNotNull('notes.text') + ->where('notes.text', '!=', '') + ->where('noteable_type', Budget::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); + foreach ($notes as $note) { + $this->notes[(int)$note['noteable_id']] = (string)$note['text']; + } + Log::debug(sprintf('Enrich with %d note(s)', count($this->notes))); } private function collectObjectGroups(): void { - $set = DB::table('object_groupables') - ->whereIn('object_groupable_id', $this->ids) - ->where('object_groupable_type', Budget::class) - ->get(['object_groupable_id', 'object_group_id']) - ; + $set = DB::table('object_groupables') + ->whereIn('object_groupable_id', $this->ids) + ->where('object_groupable_type', Budget::class) + ->get(['object_groupable_id', 'object_group_id']); - $ids = array_unique($set->pluck('object_group_id')->toArray()); + $ids = array_unique($set->pluck('object_group_id')->toArray()); foreach ($set as $entry) { $this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id; diff --git a/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php b/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php index 0ba9e1f1a3..f0a7fd3479 100644 --- a/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php +++ b/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php @@ -40,19 +40,19 @@ use Illuminate\Support\Facades\Log; class BudgetLimitEnrichment implements EnrichmentInterface { - private User $user; - private UserGroup $userGroup; // @phpstan-ignore-line private Collection $collection; - private array $ids = []; - private array $notes = []; - private Carbon $start; + private bool $convertToPrimary = true; // @phpstan-ignore-line + private array $currencies = []; + private array $currencyIds = []; private Carbon $end; private array $expenses = []; + private array $ids = []; + private array $notes = []; private array $pcExpenses = []; - private array $currencyIds = []; - private array $currencies = []; - private bool $convertToPrimary = true; private readonly TransactionCurrency $primaryCurrency; + private Carbon $start; + private User $user; + private UserGroup $userGroup; public function __construct() { @@ -73,7 +73,7 @@ class BudgetLimitEnrichment implements EnrichmentInterface return $this->collection; } - public function enrichSingle(array|Model $model): array|Model + public function enrichSingle(array | Model $model): array | Model { Log::debug(__METHOD__); $collection = new Collection()->push($model); @@ -93,36 +93,6 @@ class BudgetLimitEnrichment implements EnrichmentInterface $this->userGroup = $userGroup; } - private function collectIds(): void - { - $this->start = $this->collection->min('start_date') ?? Carbon::now()->startOfMonth(); - $this->end = $this->collection->max('end_date') ?? Carbon::now()->endOfMonth(); - - /** @var BudgetLimit $limit */ - foreach ($this->collection as $limit) { - $id = (int)$limit->id; - $this->ids[] = $id; - if (0 !== (int)$limit->transaction_currency_id) { - $this->currencyIds[$id] = (int)$limit->transaction_currency_id; - } - } - $this->ids = array_unique($this->ids); - $this->currencyIds = array_unique($this->currencyIds); - } - - private function collectNotes(): void - { - $notes = Note::query()->whereIn('noteable_id', $this->ids) - ->whereNotNull('notes.text') - ->where('notes.text', '!=', '') - ->where('noteable_type', BudgetLimit::class)->get(['notes.noteable_id', 'notes.text'])->toArray() - ; - foreach ($notes as $note) { - $this->notes[(int)$note['noteable_id']] = (string)$note['text']; - } - Log::debug(sprintf('Enrich with %d note(s)', count($this->notes))); - } - private function appendCollectedData(): void { $this->collection = $this->collection->map(function (BudgetLimit $item) { @@ -145,12 +115,12 @@ class BudgetLimitEnrichment implements EnrichmentInterface private function collectBudgets(): void { - $budgetIds = $this->collection->pluck('budget_id')->unique()->toArray(); - $budgets = Budget::whereIn('id', $budgetIds)->get(); + $budgetIds = $this->collection->pluck('budget_id')->unique()->toArray(); + $budgets = Budget::whereIn('id', $budgetIds)->get(); $repository = app(OperationsRepository::class); $repository->setUser($this->user); - $expenses = $repository->collectExpenses($this->start, $this->end, null, $budgets, null); + $expenses = $repository->collectExpenses($this->start, $this->end, null, $budgets, null); /** @var BudgetLimit $budgetLimit */ foreach ($this->collection as $budgetLimit) { @@ -179,26 +149,55 @@ class BudgetLimitEnrichment implements EnrichmentInterface } } - private function stringifyIds(): void + private function collectIds(): void { - $this->expenses = array_map(fn ($first) => array_map(function ($second) { - $second['currency_id'] = (string)($second['currency_id'] ?? 0); + $this->start = $this->collection->min('start_date') ?? Carbon::now()->startOfMonth(); + $this->end = $this->collection->max('end_date') ?? Carbon::now()->endOfMonth(); - return $second; - }, $first), $this->expenses); + /** @var BudgetLimit $limit */ + foreach ($this->collection as $limit) { + $id = (int)$limit->id; + $this->ids[] = $id; + if (0 !== (int)$limit->transaction_currency_id) { + $this->currencyIds[$id] = (int)$limit->transaction_currency_id; + } + } + $this->ids = array_unique($this->ids); + $this->currencyIds = array_unique($this->currencyIds); + } - $this->pcExpenses = array_map(fn ($first) => array_map(function ($second) { - $second['currency_id'] = (string)($second['currency_id'] ?? 0); - - return $second; - }, $first), $this->expenses); + private function collectNotes(): void + { + $notes = Note::query()->whereIn('noteable_id', $this->ids) + ->whereNotNull('notes.text') + ->where('notes.text', '!=', '') + ->where('noteable_type', BudgetLimit::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); + foreach ($notes as $note) { + $this->notes[(int)$note['noteable_id']] = (string)$note['text']; + } + Log::debug(sprintf('Enrich with %d note(s)', count($this->notes))); } private function filterToBudget(array $expenses, int $budget): array { - $result = array_filter($expenses, fn (array $item) => (int)$item['budget_id'] === $budget); + $result = array_filter($expenses, fn(array $item) => (int)$item['budget_id'] === $budget); Log::debug(sprintf('filterToBudget for budget #%d, from %d to %d items', $budget, count($expenses), count($result))); return $result; } + + private function stringifyIds(): void + { + $this->expenses = array_map(fn($first) => array_map(function ($second) { + $second['currency_id'] = (string)($second['currency_id'] ?? 0); + + return $second; + }, $first), $this->expenses); + + $this->pcExpenses = array_map(fn($first) => array_map(function ($second) { + $second['currency_id'] = (string)($second['currency_id'] ?? 0); + + return $second; + }, $first), $this->expenses); + } } diff --git a/app/Support/JsonApi/Enrichments/CategoryEnrichment.php b/app/Support/JsonApi/Enrichments/CategoryEnrichment.php index bbe24f94c2..074747011b 100644 --- a/app/Support/JsonApi/Enrichments/CategoryEnrichment.php +++ b/app/Support/JsonApi/Enrichments/CategoryEnrichment.php @@ -38,18 +38,18 @@ use Illuminate\Support\Facades\Log; class CategoryEnrichment implements EnrichmentInterface { private Collection $collection; - private User $user; - private UserGroup $userGroup; + private array $earned = []; + private ?Carbon $end = null; private array $ids = []; private array $notes = []; - private ?Carbon $start = null; - private ?Carbon $end = null; - private array $spent = []; - private array $pcSpent = []; - private array $earned = []; private array $pcEarned = []; - private array $transfers = []; + private array $pcSpent = []; private array $pcTransfers = []; + private array $spent = []; + private ?Carbon $start = null; + private array $transfers = []; + private User $user; + private UserGroup $userGroup; public function enrich(Collection $collection): Collection { @@ -62,7 +62,7 @@ class CategoryEnrichment implements EnrichmentInterface return $collection; } - public function enrichSingle(array|Model $model): array|Model + public function enrichSingle(array | Model $model): array | Model { Log::debug(__METHOD__); $collection = new Collection()->push($model); @@ -71,6 +71,16 @@ class CategoryEnrichment implements EnrichmentInterface return $collection->first(); } + public function setEnd(?Carbon $end): void + { + $this->end = $end; + } + + public function setStart(?Carbon $start): void + { + $this->start = $start; + } + public function setUser(User $user): void { $this->user = $user; @@ -82,15 +92,6 @@ class CategoryEnrichment implements EnrichmentInterface $this->userGroup = $userGroup; } - private function collectIds(): void - { - /** @var Category $category */ - foreach ($this->collection as $category) { - $this->ids[] = (int)$category->id; - } - $this->ids = array_unique($this->ids); - } - private function appendCollectedData(): void { $this->collection = $this->collection->map(function (Category $item) { @@ -110,23 +111,21 @@ class CategoryEnrichment implements EnrichmentInterface }); } - public function setEnd(?Carbon $end): void + private function collectIds(): void { - $this->end = $end; - } - - public function setStart(?Carbon $start): void - { - $this->start = $start; + /** @var Category $category */ + foreach ($this->collection as $category) { + $this->ids[] = (int)$category->id; + } + $this->ids = array_unique($this->ids); } private function collectNotes(): void { $notes = Note::query()->whereIn('noteable_id', $this->ids) - ->whereNotNull('notes.text') - ->where('notes.text', '!=', '') - ->where('noteable_type', Category::class)->get(['notes.noteable_id', 'notes.text'])->toArray() - ; + ->whereNotNull('notes.text') + ->where('notes.text', '!=', '') + ->where('noteable_type', Category::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); foreach ($notes as $note) { $this->notes[(int)$note['noteable_id']] = (string)$note['text']; } @@ -140,9 +139,9 @@ class CategoryEnrichment implements EnrichmentInterface $opsRepository = app(OperationsRepositoryInterface::class); $opsRepository->setUser($this->user); $opsRepository->setUserGroup($this->userGroup); - $expenses = $opsRepository->collectExpenses($this->start, $this->end, null, $this->collection); - $income = $opsRepository->collectIncome($this->start, $this->end, null, $this->collection); - $transfers = $opsRepository->collectTransfers($this->start, $this->end, null, $this->collection); + $expenses = $opsRepository->collectExpenses($this->start, $this->end, null, $this->collection); + $income = $opsRepository->collectIncome($this->start, $this->end, null, $this->collection); + $transfers = $opsRepository->collectTransfers($this->start, $this->end, null, $this->collection); foreach ($this->collection as $item) { $id = (int)$item->id; $this->spent[$id] = array_values($opsRepository->sumCollectedTransactionsByCategory($expenses, $item, 'negative', false)); diff --git a/app/Support/JsonApi/Enrichments/EnrichmentInterface.php b/app/Support/JsonApi/Enrichments/EnrichmentInterface.php index 0ccfb7c060..e93ddcf283 100644 --- a/app/Support/JsonApi/Enrichments/EnrichmentInterface.php +++ b/app/Support/JsonApi/Enrichments/EnrichmentInterface.php @@ -33,7 +33,7 @@ interface EnrichmentInterface { public function enrich(Collection $collection): Collection; - public function enrichSingle(array|Model $model): array|Model; + public function enrichSingle(array | Model $model): array | Model; public function setUser(User $user): void; diff --git a/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php b/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php index bacf8d12c3..aebdde8f67 100644 --- a/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php +++ b/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php @@ -43,20 +43,20 @@ use Illuminate\Support\Facades\Log; class PiggyBankEnrichment implements EnrichmentInterface { - private User $user; // @phpstan-ignore-line - private UserGroup $userGroup; // @phpstan-ignore-line - private Collection $collection; - private array $ids = []; - private array $currencyIds = []; - private array $currencies = []; - private array $accountIds = []; + private array $accountIds = []; // @phpstan-ignore-line + private array $accounts = []; // @phpstan-ignore-line + private array $amounts = []; + private Collection $collection; + private array $currencies = []; + private array $currencyIds = []; + private array $ids = []; // private array $accountCurrencies = []; - private array $notes = []; - private array $mappedObjects = []; + private array $mappedObjects = []; + private array $notes = []; + private array $objectGroups = []; private readonly TransactionCurrency $primaryCurrency; - private array $amounts = []; - private array $accounts = []; - private array $objectGroups = []; + private User $user; + private UserGroup $userGroup; public function __construct() { @@ -77,7 +77,7 @@ class PiggyBankEnrichment implements EnrichmentInterface return $this->collection; } - public function enrichSingle(array|Model $model): array|Model + public function enrichSingle(array | Model $model): array | Model { Log::debug(__METHOD__); $collection = new Collection()->push($model); @@ -97,80 +97,17 @@ class PiggyBankEnrichment implements EnrichmentInterface $this->userGroup = $userGroup; } - private function collectIds(): void - { - /** @var PiggyBank $piggy */ - foreach ($this->collection as $piggy) { - $id = (int)$piggy->id; - $this->ids[] = $id; - $this->currencyIds[$id] = (int)$piggy->transaction_currency_id; - } - $this->ids = array_unique($this->ids); - - // collect currencies. - $currencies = TransactionCurrency::whereIn('id', $this->currencyIds)->get(); - foreach ($currencies as $currency) { - $this->currencies[(int)$currency->id] = $currency; - } - - // collect accounts - $set = DB::table('account_piggy_bank')->whereIn('piggy_bank_id', $this->ids)->get(['piggy_bank_id', 'account_id', 'current_amount', 'native_current_amount']); - foreach ($set as $item) { - $id = (int)$item->piggy_bank_id; - $accountId = (int)$item->account_id; - $this->amounts[$id] ??= []; - if (!array_key_exists($id, $this->accountIds)) { - $this->accountIds[$id] = (int)$item->account_id; - } - if (!array_key_exists($accountId, $this->amounts[$id])) { - $this->amounts[$id][$accountId] = [ - 'current_amount' => '0', - 'pc_current_amount' => '0', - ]; - } - $this->amounts[$id][$accountId]['current_amount'] = bcadd($this->amounts[$id][$accountId]['current_amount'], (string) $item->current_amount); - if (null !== $this->amounts[$id][$accountId]['pc_current_amount'] && null !== $item->native_current_amount) { - $this->amounts[$id][$accountId]['pc_current_amount'] = bcadd($this->amounts[$id][$accountId]['pc_current_amount'], (string) $item->native_current_amount); - } - } - - // get account currency preference for ALL. - $set = AccountMeta::whereIn('account_id', array_values($this->accountIds))->where('name', 'currency_id')->get(); - - /** @var AccountMeta $item */ - foreach ($set as $item) { - $accountId = (int)$item->account_id; - $currencyId = (int)$item->data; - if (!array_key_exists($currencyId, $this->currencies)) { - $this->currencies[$currencyId] = Amount::getTransactionCurrencyById($currencyId); - } - // $this->accountCurrencies[$accountId] = $this->currencies[$currencyId]; - } - - // get account info. - $set = Account::whereIn('id', array_values($this->accountIds))->get(); - - /** @var Account $item */ - foreach ($set as $item) { - $id = (int)$item->id; - $this->accounts[$id] = [ - 'id' => $id, - 'name' => $item->name, - ]; - } - } - private function appendCollectedData(): void { $this->collection = $this->collection->map(function (PiggyBank $item) { - $id = (int)$item->id; - $currencyId = (int)$item->transaction_currency_id; - $currency = $this->currencies[$currencyId] ?? $this->primaryCurrency; - $targetAmount = null; + $id = (int)$item->id; + $currencyId = (int)$item->transaction_currency_id; + $currency = $this->currencies[$currencyId] ?? $this->primaryCurrency; + $targetAmount = null; if (0 !== bccomp($item->target_amount, '0')) { $targetAmount = $item->target_amount; } - $meta = [ + $meta = [ 'notes' => $this->notes[$id] ?? null, 'currency' => $this->currencies[$currencyId] ?? null, // 'auto_budget' => $this->autoBudgets[$id] ?? null, @@ -193,23 +130,23 @@ class PiggyBankEnrichment implements EnrichmentInterface // add object group if available if (array_key_exists($id, $this->mappedObjects)) { $key = $this->mappedObjects[$id]; - $meta['object_group_id'] = (string) $this->objectGroups[$key]['id']; + $meta['object_group_id'] = (string)$this->objectGroups[$key]['id']; $meta['object_group_title'] = $this->objectGroups[$key]['title']; $meta['object_group_order'] = $this->objectGroups[$key]['order']; } // add current amount(s). foreach ($this->amounts[$id] as $accountId => $row) { - $meta['accounts'][] = [ + $meta['accounts'][] = [ 'account_id' => (string)$accountId, 'name' => $this->accounts[$accountId]['name'] ?? '', 'current_amount' => Steam::bcround($row['current_amount'], $currency->decimal_places), 'pc_current_amount' => Steam::bcround($row['pc_current_amount'], $this->primaryCurrency->decimal_places), ]; - $meta['current_amount'] = bcadd($meta['current_amount'], $row['current_amount']); + $meta['current_amount'] = bcadd($meta['current_amount'], $row['current_amount']); // only add pc_current_amount when the pc_current_amount is set $meta['pc_current_amount'] = null === $row['pc_current_amount'] ? null : bcadd($meta['pc_current_amount'], $row['pc_current_amount']); } - $meta['current_amount'] = Steam::bcround($meta['current_amount'], $currency->decimal_places); + $meta['current_amount'] = Steam::bcround($meta['current_amount'], $currency->decimal_places); // only round this number when pc_current_amount is set. $meta['pc_current_amount'] = null === $meta['pc_current_amount'] ? null : Steam::bcround($meta['pc_current_amount'], $this->primaryCurrency->decimal_places); @@ -223,19 +160,83 @@ class PiggyBankEnrichment implements EnrichmentInterface $meta['save_per_month'] = Steam::bcround($this->getSuggestedMonthlyAmount($item->start_date, $item->target_date, $meta['target_amount'], $meta['current_amount']), $currency->decimal_places); $meta['pc_save_per_month'] = Steam::bcround($this->getSuggestedMonthlyAmount($item->start_date, $item->target_date, $meta['pc_target_amount'], $meta['pc_current_amount']), $currency->decimal_places); - $item->meta = $meta; + $item->meta = $meta; return $item; }); } + private function collectCurrentAmounts(): void {} + + private function collectIds(): void + { + /** @var PiggyBank $piggy */ + foreach ($this->collection as $piggy) { + $id = (int)$piggy->id; + $this->ids[] = $id; + $this->currencyIds[$id] = (int)$piggy->transaction_currency_id; + } + $this->ids = array_unique($this->ids); + + // collect currencies. + $currencies = TransactionCurrency::whereIn('id', $this->currencyIds)->get(); + foreach ($currencies as $currency) { + $this->currencies[(int)$currency->id] = $currency; + } + + // collect accounts + $set = DB::table('account_piggy_bank')->whereIn('piggy_bank_id', $this->ids)->get(['piggy_bank_id', 'account_id', 'current_amount', 'native_current_amount']); + foreach ($set as $item) { + $id = (int)$item->piggy_bank_id; + $accountId = (int)$item->account_id; + $this->amounts[$id] ??= []; + if (!array_key_exists($id, $this->accountIds)) { + $this->accountIds[$id] = (int)$item->account_id; + } + if (!array_key_exists($accountId, $this->amounts[$id])) { + $this->amounts[$id][$accountId] = [ + 'current_amount' => '0', + 'pc_current_amount' => '0', + ]; + } + $this->amounts[$id][$accountId]['current_amount'] = bcadd($this->amounts[$id][$accountId]['current_amount'], (string)$item->current_amount); + if (null !== $this->amounts[$id][$accountId]['pc_current_amount'] && null !== $item->native_current_amount) { + $this->amounts[$id][$accountId]['pc_current_amount'] = bcadd($this->amounts[$id][$accountId]['pc_current_amount'], (string)$item->native_current_amount); + } + } + + // get account currency preference for ALL. + $set = AccountMeta::whereIn('account_id', array_values($this->accountIds))->where('name', 'currency_id')->get(); + + /** @var AccountMeta $item */ + foreach ($set as $item) { + $accountId = (int)$item->account_id; + $currencyId = (int)$item->data; + if (!array_key_exists($currencyId, $this->currencies)) { + $this->currencies[$currencyId] = Amount::getTransactionCurrencyById($currencyId); + } + // $this->accountCurrencies[$accountId] = $this->currencies[$currencyId]; + } + + // get account info. + $set = Account::whereIn('id', array_values($this->accountIds))->get(); + + /** @var Account $item */ + foreach ($set as $item) { + $id = (int)$item->id; + $this->accounts[$id] = [ + 'id' => $id, + 'name' => $item->name, + ]; + } + } + private function collectNotes(): void { $notes = Note::query()->whereIn('noteable_id', $this->ids) - ->whereNotNull('notes.text') - ->where('notes.text', '!=', '') - ->where('noteable_type', PiggyBank::class)->get(['notes.noteable_id', 'notes.text'])->toArray() - ; + ->whereNotNull('notes.text') + ->where('notes.text', '!=', '') + ->where('noteable_type', PiggyBank::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); foreach ($notes as $note) { $this->notes[(int)$note['noteable_id']] = (string)$note['text']; } @@ -244,13 +245,12 @@ class PiggyBankEnrichment implements EnrichmentInterface private function collectObjectGroups(): void { - $set = DB::table('object_groupables') - ->whereIn('object_groupable_id', $this->ids) - ->where('object_groupable_type', PiggyBank::class) - ->get(['object_groupable_id', 'object_group_id']) - ; + $set = DB::table('object_groupables') + ->whereIn('object_groupable_id', $this->ids) + ->where('object_groupable_type', PiggyBank::class) + ->get(['object_groupable_id', 'object_group_id']); - $ids = array_unique($set->pluck('object_group_id')->toArray()); + $ids = array_unique($set->pluck('object_group_id')->toArray()); foreach ($set as $entry) { $this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id; @@ -264,8 +264,6 @@ class PiggyBankEnrichment implements EnrichmentInterface } } - private function collectCurrentAmounts(): void {} - /** * Returns the suggested amount the user should save per month, or "". */ diff --git a/app/Support/JsonApi/Enrichments/PiggyBankEventEnrichment.php b/app/Support/JsonApi/Enrichments/PiggyBankEventEnrichment.php index fad6293f90..8758dfe94d 100644 --- a/app/Support/JsonApi/Enrichments/PiggyBankEventEnrichment.php +++ b/app/Support/JsonApi/Enrichments/PiggyBankEventEnrichment.php @@ -38,16 +38,16 @@ use Illuminate\Support\Facades\Log; class PiggyBankEventEnrichment implements EnrichmentInterface { - private User $user; // @phpstan-ignore-line - private UserGroup $userGroup; // @phpstan-ignore-line + private array $accountCurrencies = []; // @phpstan-ignore-line + private array $accountIds = []; // @phpstan-ignore-line private Collection $collection; + private array $currencies = []; + private array $groupIds = []; private array $ids = []; private array $journalIds = []; - private array $groupIds = []; - private array $accountIds = []; private array $piggyBankIds = []; - private array $accountCurrencies = []; - private array $currencies = []; + private User $user; + private UserGroup $userGroup; // private bool $convertToPrimary = false; // private TransactionCurrency $primaryCurrency; @@ -66,7 +66,7 @@ class PiggyBankEventEnrichment implements EnrichmentInterface return $this->collection; } - public function enrichSingle(array|Model $model): array|Model + public function enrichSingle(array | Model $model): array | Model { Log::debug(__METHOD__); $collection = new Collection()->push($model); @@ -86,53 +86,13 @@ class PiggyBankEventEnrichment implements EnrichmentInterface $this->userGroup = $userGroup; } - private function collectIds(): void - { - /** @var PiggyBankEvent $event */ - foreach ($this->collection as $event) { - $this->ids[] = (int)$event->id; - $this->journalIds[(int)$event->id] = (int)$event->transaction_journal_id; - $this->piggyBankIds[(int)$event->id] = (int)$event->piggy_bank_id; - } - $this->ids = array_unique($this->ids); - // collect groups with journal info. - $set = TransactionJournal::whereIn('id', $this->journalIds)->get(['id', 'transaction_group_id']); - - /** @var TransactionJournal $item */ - foreach ($set as $item) { - $this->groupIds[(int)$item->id] = (int)$item->transaction_group_id; - } - - // collect account info. - $set = DB::table('account_piggy_bank')->whereIn('piggy_bank_id', $this->piggyBankIds)->get(['piggy_bank_id', 'account_id']); - foreach ($set as $item) { - $id = (int)$item->piggy_bank_id; - if (!array_key_exists($id, $this->accountIds)) { - $this->accountIds[$id] = (int)$item->account_id; - } - } - - // get account currency preference for ALL. - $set = AccountMeta::whereIn('account_id', array_values($this->accountIds))->where('name', 'currency_id')->get(); - - /** @var AccountMeta $item */ - foreach ($set as $item) { - $accountId = (int)$item->account_id; - $currencyId = (int)$item->data; - if (!array_key_exists($currencyId, $this->currencies)) { - $this->currencies[$currencyId] = Amount::getTransactionCurrencyById($currencyId); - } - $this->accountCurrencies[$accountId] = $this->currencies[$currencyId]; - } - } - private function appendCollectedData(): void { $this->collection = $this->collection->map(function (PiggyBankEvent $item) { - $id = (int)$item->id; - $piggyId = (int)$item->piggy_bank_id; - $journalId = (int)$item->transaction_journal_id; - $currency = null; + $id = (int)$item->id; + $piggyId = (int)$item->piggy_bank_id; + $journalId = (int)$item->transaction_journal_id; + $currency = null; if (array_key_exists($piggyId, $this->accountIds)) { $accountId = $this->accountIds[$piggyId]; if (array_key_exists($accountId, $this->accountCurrencies)) { @@ -149,4 +109,44 @@ class PiggyBankEventEnrichment implements EnrichmentInterface }); } + + private function collectIds(): void + { + /** @var PiggyBankEvent $event */ + foreach ($this->collection as $event) { + $this->ids[] = (int)$event->id; + $this->journalIds[(int)$event->id] = (int)$event->transaction_journal_id; + $this->piggyBankIds[(int)$event->id] = (int)$event->piggy_bank_id; + } + $this->ids = array_unique($this->ids); + // collect groups with journal info. + $set = TransactionJournal::whereIn('id', $this->journalIds)->get(['id', 'transaction_group_id']); + + /** @var TransactionJournal $item */ + foreach ($set as $item) { + $this->groupIds[(int)$item->id] = (int)$item->transaction_group_id; + } + + // collect account info. + $set = DB::table('account_piggy_bank')->whereIn('piggy_bank_id', $this->piggyBankIds)->get(['piggy_bank_id', 'account_id']); + foreach ($set as $item) { + $id = (int)$item->piggy_bank_id; + if (!array_key_exists($id, $this->accountIds)) { + $this->accountIds[$id] = (int)$item->account_id; + } + } + + // get account currency preference for ALL. + $set = AccountMeta::whereIn('account_id', array_values($this->accountIds))->where('name', 'currency_id')->get(); + + /** @var AccountMeta $item */ + foreach ($set as $item) { + $accountId = (int)$item->account_id; + $currencyId = (int)$item->data; + if (!array_key_exists($currencyId, $this->currencies)) { + $this->currencies[$currencyId] = Amount::getTransactionCurrencyById($currencyId); + } + $this->accountCurrencies[$accountId] = $this->currencies[$currencyId]; + } + } } diff --git a/app/Support/JsonApi/Enrichments/RecurringEnrichment.php b/app/Support/JsonApi/Enrichments/RecurringEnrichment.php index c8a3d8707a..30484f5388 100644 --- a/app/Support/JsonApi/Enrichments/RecurringEnrichment.php +++ b/app/Support/JsonApi/Enrichments/RecurringEnrichment.php @@ -51,30 +51,29 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; - use function Safe\json_decode; class RecurringEnrichment implements EnrichmentInterface { - private Collection $collection; - private array $ids = []; + private array $accounts = []; + private Collection $collection; // private array $transactionTypeIds = []; // private array $transactionTypes = []; - private array $notes = []; - private array $repetitions = []; - private array $transactions = []; - private User $user; - private UserGroup $userGroup; - private string $language = 'en_US'; - private array $currencyIds = []; - private array $foreignCurrencyIds = []; - private array $sourceAccountIds = []; - private array $destinationAccountIds = []; - private array $accounts = []; - private array $currencies = []; - private array $recurrenceIds = []; + private bool $convertToPrimary = false; + private array $currencies = []; + private array $currencyIds = []; + private array $destinationAccountIds = []; + private array $foreignCurrencyIds = []; + private array $ids = []; + private string $language = 'en_US'; + private array $notes = []; private readonly TransactionCurrency $primaryCurrency; - private bool $convertToPrimary = false; + private array $recurrenceIds = []; + private array $repetitions = []; + private array $sourceAccountIds = []; + private array $transactions = []; + private User $user; + private UserGroup $userGroup; public function __construct() { @@ -98,7 +97,7 @@ class RecurringEnrichment implements EnrichmentInterface return $this->collection; } - public function enrichSingle(array|Model $model): array|Model + public function enrichSingle(array | Model $model): array | Model { Log::debug(__METHOD__); $collection = new Collection()->push($model); @@ -107,139 +106,6 @@ class RecurringEnrichment implements EnrichmentInterface return $collection->first(); } - public function setUser(User $user): void - { - $this->user = $user; - $this->setUserGroup($user->userGroup); - $this->getLanguage(); - } - - public function setUserGroup(UserGroup $userGroup): void - { - $this->userGroup = $userGroup; - } - - private function collectIds(): void - { - /** @var Recurrence $recurrence */ - foreach ($this->collection as $recurrence) { - $id = (int)$recurrence->id; - // $typeId = (int)$recurrence->transaction_type_id; - $this->ids[] = $id; - // $this->transactionTypeIds[$id] = $typeId; - } - $this->ids = array_unique($this->ids); - - // collect transaction types. - // $transactionTypes = TransactionType::whereIn('id', array_unique($this->transactionTypeIds))->get(); - // foreach ($transactionTypes as $transactionType) { - // $id = (int)$transactionType->id; - // $this->transactionTypes[$id] = TransactionTypeEnum::from($transactionType->type); - // } - } - - private function collectRepetitions(): void - { - Log::debug('Start of enrichment: collectRepetitions()'); - $repository = app(RecurringRepositoryInterface::class); - $repository->setUserGroup($this->userGroup); - $set = RecurrenceRepetition::whereIn('recurrence_id', $this->ids)->get(); - - /** @var RecurrenceRepetition $repetition */ - foreach ($set as $repetition) { - $recurrence = $this->collection->filter(fn (Recurrence $item) => (int)$item->id === (int)$repetition->recurrence_id)->first(); - $fromDate = clone ($recurrence->latest_date ?? $recurrence->first_date); - $id = (int)$repetition->recurrence_id; - $repId = (int)$repetition->id; - $this->repetitions[$id] ??= []; - - // get the (future) occurrences for this specific type of repetition: - $amount = 'daily' === $repetition->repetition_type ? 9 : 5; - $set = $repository->getXOccurrencesSince($repetition, $fromDate, now(config('app.timezone')), $amount); - $occurrences = []; - - /** @var Carbon $carbon */ - foreach ($set as $carbon) { - $occurrences[] = $carbon->toAtomString(); - } - $this->repetitions[$id][$repId] = [ - 'id' => (string)$repId, - 'created_at' => $repetition->created_at->toAtomString(), - 'updated_at' => $repetition->updated_at->toAtomString(), - 'type' => $repetition->repetition_type, - 'moment' => (string)$repetition->repetition_moment, - 'skip' => (int)$repetition->repetition_skip, - 'weekend' => RecurrenceRepetitionWeekend::from((int)$repetition->weekend)->value, - 'description' => $this->getRepetitionDescription($repetition), - 'occurrences' => $occurrences, - ]; - } - Log::debug('End of enrichment: collectRepetitions()'); - } - - private function collectTransactions(): void - { - $set = RecurrenceTransaction::whereIn('recurrence_id', $this->ids)->get(); - - /** @var RecurrenceTransaction $transaction */ - foreach ($set as $transaction) { - $id = (int)$transaction->recurrence_id; - $transactionId = (int)$transaction->id; - $this->recurrenceIds[$transactionId] = $id; - $this->transactions[$id] ??= []; - $amount = $transaction->amount; - $foreignAmount = $transaction->foreign_amount; - - $this->transactions[$id][$transactionId] = [ - 'id' => (string)$transactionId, - // 'recurrence_id' => $id, - 'transaction_currency_id' => (int)$transaction->transaction_currency_id, - 'foreign_currency_id' => null === $transaction->foreign_currency_id ? null : (int)$transaction->foreign_currency_id, - 'source_id' => (int)$transaction->source_id, - 'object_has_currency_setting' => true, - 'destination_id' => (int)$transaction->destination_id, - 'amount' => $amount, - 'foreign_amount' => $foreignAmount, - 'pc_amount' => null, - 'pc_foreign_amount' => null, - 'description' => $transaction->description, - 'tags' => [], - 'category_id' => null, - 'category_name' => null, - 'budget_id' => null, - 'budget_name' => null, - 'piggy_bank_id' => null, - 'piggy_bank_name' => null, - 'subscription_id' => null, - 'subscription_name' => null, - - ]; - // collect all kinds of meta data to be collected later. - $this->currencyIds[$transactionId] = (int)$transaction->transaction_currency_id; - $this->sourceAccountIds[$transactionId] = (int)$transaction->source_id; - $this->destinationAccountIds[$transactionId] = (int)$transaction->destination_id; - if (null !== $transaction->foreign_currency_id) { - $this->foreignCurrencyIds[$transactionId] = (int)$transaction->foreign_currency_id; - } - } - } - - private function appendCollectedData(): void - { - $this->collection = $this->collection->map(function (Recurrence $item) { - $id = (int)$item->id; - $meta = [ - 'notes' => $this->notes[$id] ?? null, - 'repetitions' => array_values($this->repetitions[$id] ?? []), - 'transactions' => $this->processTransactions(array_values($this->transactions[$id] ?? [])), - ]; - - $item->meta = $meta; - - return $item; - }); - } - /** * Parse the repetition in a string that is user readable. * TODO duplicate with repository. @@ -265,7 +131,7 @@ class RecurringEnrichment implements EnrichmentInterface return (string)trans('firefly.recurring_monthly', ['dayOfMonth' => $repetition->repetition_moment, 'skip' => $repetition->repetition_skip - 1], $this->language); } if ('ndom' === $repetition->repetition_type) { - $parts = explode(',', $repetition->repetition_moment); + $parts = explode(',', $repetition->repetition_moment); // first part is number of week, second is weekday. $dayOfWeek = trans(sprintf('config.dow_%s', $parts[1]), [], $this->language); if ($repetition->repetition_skip > 0) { @@ -282,7 +148,7 @@ class RecurringEnrichment implements EnrichmentInterface } // $diffInYears = (int)$today->diffInYears($repDate, true); // $repDate->addYears($diffInYears); // technically not necessary. - $string = $repDate->isoFormat((string)trans('config.month_and_day_no_year_js')); + $string = $repDate->isoFormat((string)trans('config.month_and_day_no_year_js')); return (string)trans('firefly.recurring_yearly', ['date' => $string], $this->language); } @@ -290,96 +156,32 @@ class RecurringEnrichment implements EnrichmentInterface return ''; } - private function getLanguage(): void + public function setUser(User $user): void { - /** @var Preference $preference */ - $preference = Preferences::getForUser($this->user, 'language', config('firefly.default_language', 'en_US')); - $language = $preference->data; - if (is_array($language)) { - $language = 'en_US'; - } - $language = (string)$language; - $this->language = $language; + $this->user = $user; + $this->setUserGroup($user->userGroup); + $this->getLanguage(); } - private function collectCurrencies(): void + public function setUserGroup(UserGroup $userGroup): void { - $all = array_merge(array_unique($this->currencyIds), array_unique($this->foreignCurrencyIds)); - $currencies = TransactionCurrency::whereIn('id', array_unique($all))->get(); - foreach ($currencies as $currency) { - $id = (int)$currency->id; - $this->currencies[$id] = $currency; - } + $this->userGroup = $userGroup; } - private function processTransactions(array $transactions): array + private function appendCollectedData(): void { - $return = []; - $converter = new ExchangeRateConverter(); - foreach ($transactions as $transaction) { - $currencyId = $transaction['transaction_currency_id']; - $pcAmount = null; - $pcForeignAmount = null; - // set the same amount in the primary currency, if both are the same anyway. - if (true === $this->convertToPrimary && $currencyId === (int)$this->primaryCurrency->id) { - $pcAmount = $transaction['amount']; - } - // convert the amount to the primary currency, if it is not the same. - if (true === $this->convertToPrimary && $currencyId !== (int)$this->primaryCurrency->id) { - $pcAmount = $converter->convert($this->currencies[$currencyId], $this->primaryCurrency, today(), $transaction['amount']); - } - if (null !== $transaction['foreign_amount'] && null !== $transaction['foreign_currency_id']) { - $foreignCurrencyId = $transaction['foreign_currency_id']; - if ($foreignCurrencyId !== $this->primaryCurrency->id) { - $pcForeignAmount = $converter->convert($this->currencies[$foreignCurrencyId], $this->primaryCurrency, today(), $transaction['foreign_amount']); - } - } + $this->collection = $this->collection->map(function (Recurrence $item) { + $id = (int)$item->id; + $meta = [ + 'notes' => $this->notes[$id] ?? null, + 'repetitions' => array_values($this->repetitions[$id] ?? []), + 'transactions' => $this->processTransactions(array_values($this->transactions[$id] ?? [])), + ]; - $transaction['pc_amount'] = $pcAmount; - $transaction['pc_foreign_amount'] = $pcForeignAmount; + $item->meta = $meta; - $sourceId = $transaction['source_id']; - $transaction['source_name'] = $this->accounts[$sourceId]->name; - $transaction['source_iban'] = $this->accounts[$sourceId]->iban; - $transaction['source_type'] = $this->accounts[$sourceId]->accountType->type; - $transaction['source_id'] = (string)$transaction['source_id']; - - $destId = $transaction['destination_id']; - $transaction['destination_name'] = $this->accounts[$destId]->name; - $transaction['destination_iban'] = $this->accounts[$destId]->iban; - $transaction['destination_type'] = $this->accounts[$destId]->accountType->type; - $transaction['destination_id'] = (string)$transaction['destination_id']; - - $transaction['currency_id'] = (string)$currencyId; - $transaction['currency_name'] = $this->currencies[$currencyId]->name; - $transaction['currency_code'] = $this->currencies[$currencyId]->code; - $transaction['currency_symbol'] = $this->currencies[$currencyId]->symbol; - $transaction['currency_decimal_places'] = $this->currencies[$currencyId]->decimal_places; - - $transaction['primary_currency_id'] = (string)$this->primaryCurrency->id; - $transaction['primary_currency_name'] = $this->primaryCurrency->name; - $transaction['primary_currency_code'] = $this->primaryCurrency->code; - $transaction['primary_currency_symbol'] = $this->primaryCurrency->symbol; - $transaction['primary_currency_decimal_places'] = $this->primaryCurrency->decimal_places; - - // $transaction['foreign_currency_id'] = null; - $transaction['foreign_currency_name'] = null; - $transaction['foreign_currency_code'] = null; - $transaction['foreign_currency_symbol'] = null; - $transaction['foreign_currency_decimal_places'] = null; - if (null !== $transaction['foreign_currency_id']) { - $currencyId = $transaction['foreign_currency_id']; - $transaction['foreign_currency_id'] = (string)$currencyId; - $transaction['foreign_currency_name'] = $this->currencies[$currencyId]->name; - $transaction['foreign_currency_code'] = $this->currencies[$currencyId]->code; - $transaction['foreign_currency_symbol'] = $this->currencies[$currencyId]->symbol; - $transaction['foreign_currency_decimal_places'] = $this->currencies[$currencyId]->decimal_places; - } - unset($transaction['transaction_currency_id']); - $return[] = $transaction; - } - - return $return; + return $item; + }); } private function collectAccounts(): void @@ -394,10 +196,183 @@ class RecurringEnrichment implements EnrichmentInterface } } + private function collectBillInfo(array $billIds): void + { + if (0 === count($billIds)) { + return; + } + $ids = Arr::pluck($billIds, 'bill_id'); + $bills = Bill::whereIn('id', $ids)->get(); + $mapped = []; + foreach ($bills as $bill) { + $mapped[(int)$bill->id] = $bill; + } + foreach ($billIds as $info) { + $recurrenceId = $info['recurrence_id']; + $transactionId = $info['transaction_id']; + $this->transactions[$recurrenceId][$transactionId]['subscription_name'] = $mapped[$info['bill_id']]->name ?? ''; + } + } + + private function collectBudgetInfo(array $budgetIds): void + { + if (0 === count($budgetIds)) { + return; + } + $ids = Arr::pluck($budgetIds, 'budget_id'); + $categories = Budget::whereIn('id', $ids)->get(); + $mapped = []; + foreach ($categories as $category) { + $mapped[(int)$category->id] = $category; + } + foreach ($budgetIds as $info) { + $recurrenceId = $info['recurrence_id']; + $transactionId = $info['transaction_id']; + $this->transactions[$recurrenceId][$transactionId]['budget_name'] = $mapped[$info['budget_id']]->name ?? ''; + } + } + + private function collectCategoryIdInfo(array $categoryIds): void + { + if (0 === count($categoryIds)) { + return; + } + $ids = Arr::pluck($categoryIds, 'category_id'); + $categories = Category::whereIn('id', $ids)->get(); + $mapped = []; + foreach ($categories as $category) { + $mapped[(int)$category->id] = $category; + } + foreach ($categoryIds as $info) { + $recurrenceId = $info['recurrence_id']; + $transactionId = $info['transaction_id']; + $this->transactions[$recurrenceId][$transactionId]['category_name'] = $mapped[$info['category_id']]->name ?? ''; + } + } + + /** + * TODO This method does look-up in a loop. + */ + private function collectCategoryNameInfo(array $categoryNames): void + { + if (0 === count($categoryNames)) { + return; + } + $factory = app(CategoryFactory::class); + $factory->setUser($this->user); + foreach ($categoryNames as $info) { + $recurrenceId = $info['recurrence_id']; + $transactionId = $info['transaction_id']; + $category = $factory->findOrCreate(null, $info['category_name']); + if (null !== $category) { + $this->transactions[$recurrenceId][$transactionId]['category_id'] = (string)$category->id; + $this->transactions[$recurrenceId][$transactionId]['category_name'] = $category->name; + } + } + } + + private function collectCurrencies(): void + { + $all = array_merge(array_unique($this->currencyIds), array_unique($this->foreignCurrencyIds)); + $currencies = TransactionCurrency::whereIn('id', array_unique($all))->get(); + foreach ($currencies as $currency) { + $id = (int)$currency->id; + $this->currencies[$id] = $currency; + } + } + + private function collectIds(): void + { + /** @var Recurrence $recurrence */ + foreach ($this->collection as $recurrence) { + $id = (int)$recurrence->id; + // $typeId = (int)$recurrence->transaction_type_id; + $this->ids[] = $id; + // $this->transactionTypeIds[$id] = $typeId; + } + $this->ids = array_unique($this->ids); + + // collect transaction types. + // $transactionTypes = TransactionType::whereIn('id', array_unique($this->transactionTypeIds))->get(); + // foreach ($transactionTypes as $transactionType) { + // $id = (int)$transactionType->id; + // $this->transactionTypes[$id] = TransactionTypeEnum::from($transactionType->type); + // } + } + + private function collectNotes(): void + { + $notes = Note::query()->whereIn('noteable_id', $this->ids) + ->whereNotNull('notes.text') + ->where('notes.text', '!=', '') + ->where('noteable_type', Recurrence::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); + foreach ($notes as $note) { + $this->notes[(int)$note['noteable_id']] = (string)$note['text']; + } + Log::debug(sprintf('Enrich with %d note(s)', count($this->notes))); + } + + private function collectPiggyBankInfo(array $piggyBankIds): void + { + if (0 === count($piggyBankIds)) { + return; + } + $ids = Arr::pluck($piggyBankIds, 'piggy_bank_id'); + $piggyBanks = PiggyBank::whereIn('id', $ids)->get(); + $mapped = []; + foreach ($piggyBanks as $piggyBank) { + $mapped[(int)$piggyBank->id] = $piggyBank; + } + foreach ($piggyBankIds as $info) { + $recurrenceId = $info['recurrence_id']; + $transactionId = $info['transaction_id']; + $this->transactions[$recurrenceId][$transactionId]['piggy_bank_name'] = $mapped[$info['piggy_bank_id']]->name ?? ''; + } + } + + private function collectRepetitions(): void + { + Log::debug('Start of enrichment: collectRepetitions()'); + $repository = app(RecurringRepositoryInterface::class); + $repository->setUserGroup($this->userGroup); + $set = RecurrenceRepetition::whereIn('recurrence_id', $this->ids)->get(); + + /** @var RecurrenceRepetition $repetition */ + foreach ($set as $repetition) { + $recurrence = $this->collection->filter(fn(Recurrence $item) => (int)$item->id === (int)$repetition->recurrence_id)->first(); + $fromDate = clone($recurrence->latest_date ?? $recurrence->first_date); + $id = (int)$repetition->recurrence_id; + $repId = (int)$repetition->id; + $this->repetitions[$id] ??= []; + + // get the (future) occurrences for this specific type of repetition: + $amount = 'daily' === $repetition->repetition_type ? 9 : 5; + $set = $repository->getXOccurrencesSince($repetition, $fromDate, now(config('app.timezone')), $amount); + $occurrences = []; + + /** @var Carbon $carbon */ + foreach ($set as $carbon) { + $occurrences[] = $carbon->toAtomString(); + } + $this->repetitions[$id][$repId] = [ + 'id' => (string)$repId, + 'created_at' => $repetition->created_at->toAtomString(), + 'updated_at' => $repetition->updated_at->toAtomString(), + 'type' => $repetition->repetition_type, + 'moment' => (string)$repetition->repetition_moment, + 'skip' => (int)$repetition->repetition_skip, + 'weekend' => RecurrenceRepetitionWeekend::from((int)$repetition->weekend)->value, + 'description' => $this->getRepetitionDescription($repetition), + 'occurrences' => $occurrences, + ]; + } + Log::debug('End of enrichment: collectRepetitions()'); + } + private function collectTransactionMetaData(): void { - $ids = array_keys($this->transactions); - $meta = RecurrenceTransactionMeta::whereNull('deleted_at')->whereIn('rt_id', $ids)->get(); + $ids = array_keys($this->transactions); + $meta = RecurrenceTransactionMeta::whereNull('deleted_at')->whereIn('rt_id', $ids)->get(); // other meta-data to be collected: $billIds = []; $piggyBankIds = []; @@ -409,8 +384,8 @@ class RecurringEnrichment implements EnrichmentInterface $transactionId = (int)$entry->rt_id; // this should refer to another array, were rtIds can be used to find the recurrence. - $recurrenceId = $this->recurrenceIds[$transactionId] ?? 0; - $name = (string)($entry->name ?? ''); + $recurrenceId = $this->recurrenceIds[$transactionId] ?? 0; + $name = (string)($entry->name ?? ''); if (0 === $recurrenceId) { Log::error(sprintf('Could not find recurrence ID for recurrence transaction ID %d', $transactionId)); @@ -504,109 +479,132 @@ class RecurringEnrichment implements EnrichmentInterface $this->collectBudgetInfo($budgetIds); } - private function collectBillInfo(array $billIds): void + private function collectTransactions(): void { - if (0 === count($billIds)) { - return; - } - $ids = Arr::pluck($billIds, 'bill_id'); - $bills = Bill::whereIn('id', $ids)->get(); - $mapped = []; - foreach ($bills as $bill) { - $mapped[(int)$bill->id] = $bill; - } - foreach ($billIds as $info) { - $recurrenceId = $info['recurrence_id']; - $transactionId = $info['transaction_id']; - $this->transactions[$recurrenceId][$transactionId]['subscription_name'] = $mapped[$info['bill_id']]->name ?? ''; - } - } + $set = RecurrenceTransaction::whereIn('recurrence_id', $this->ids)->get(); - private function collectPiggyBankInfo(array $piggyBankIds): void - { - if (0 === count($piggyBankIds)) { - return; - } - $ids = Arr::pluck($piggyBankIds, 'piggy_bank_id'); - $piggyBanks = PiggyBank::whereIn('id', $ids)->get(); - $mapped = []; - foreach ($piggyBanks as $piggyBank) { - $mapped[(int)$piggyBank->id] = $piggyBank; - } - foreach ($piggyBankIds as $info) { - $recurrenceId = $info['recurrence_id']; - $transactionId = $info['transaction_id']; - $this->transactions[$recurrenceId][$transactionId]['piggy_bank_name'] = $mapped[$info['piggy_bank_id']]->name ?? ''; - } - } + /** @var RecurrenceTransaction $transaction */ + foreach ($set as $transaction) { + $id = (int)$transaction->recurrence_id; + $transactionId = (int)$transaction->id; + $this->recurrenceIds[$transactionId] = $id; + $this->transactions[$id] ??= []; + $amount = $transaction->amount; + $foreignAmount = $transaction->foreign_amount; - private function collectCategoryIdInfo(array $categoryIds): void - { - if (0 === count($categoryIds)) { - return; - } - $ids = Arr::pluck($categoryIds, 'category_id'); - $categories = Category::whereIn('id', $ids)->get(); - $mapped = []; - foreach ($categories as $category) { - $mapped[(int)$category->id] = $category; - } - foreach ($categoryIds as $info) { - $recurrenceId = $info['recurrence_id']; - $transactionId = $info['transaction_id']; - $this->transactions[$recurrenceId][$transactionId]['category_name'] = $mapped[$info['category_id']]->name ?? ''; - } - } + $this->transactions[$id][$transactionId] = [ + 'id' => (string)$transactionId, + // 'recurrence_id' => $id, + 'transaction_currency_id' => (int)$transaction->transaction_currency_id, + 'foreign_currency_id' => null === $transaction->foreign_currency_id ? null : (int)$transaction->foreign_currency_id, + 'source_id' => (int)$transaction->source_id, + 'object_has_currency_setting' => true, + 'destination_id' => (int)$transaction->destination_id, + 'amount' => $amount, + 'foreign_amount' => $foreignAmount, + 'pc_amount' => null, + 'pc_foreign_amount' => null, + 'description' => $transaction->description, + 'tags' => [], + 'category_id' => null, + 'category_name' => null, + 'budget_id' => null, + 'budget_name' => null, + 'piggy_bank_id' => null, + 'piggy_bank_name' => null, + 'subscription_id' => null, + 'subscription_name' => null, - /** - * TODO This method does look-up in a loop. - */ - private function collectCategoryNameInfo(array $categoryNames): void - { - if (0 === count($categoryNames)) { - return; - } - $factory = app(CategoryFactory::class); - $factory->setUser($this->user); - foreach ($categoryNames as $info) { - $recurrenceId = $info['recurrence_id']; - $transactionId = $info['transaction_id']; - $category = $factory->findOrCreate(null, $info['category_name']); - if (null !== $category) { - $this->transactions[$recurrenceId][$transactionId]['category_id'] = (string)$category->id; - $this->transactions[$recurrenceId][$transactionId]['category_name'] = $category->name; + ]; + // collect all kinds of meta data to be collected later. + $this->currencyIds[$transactionId] = (int)$transaction->transaction_currency_id; + $this->sourceAccountIds[$transactionId] = (int)$transaction->source_id; + $this->destinationAccountIds[$transactionId] = (int)$transaction->destination_id; + if (null !== $transaction->foreign_currency_id) { + $this->foreignCurrencyIds[$transactionId] = (int)$transaction->foreign_currency_id; } } } - private function collectBudgetInfo(array $budgetIds): void + private function getLanguage(): void { - if (0 === count($budgetIds)) { - return; - } - $ids = Arr::pluck($budgetIds, 'budget_id'); - $categories = Budget::whereIn('id', $ids)->get(); - $mapped = []; - foreach ($categories as $category) { - $mapped[(int)$category->id] = $category; - } - foreach ($budgetIds as $info) { - $recurrenceId = $info['recurrence_id']; - $transactionId = $info['transaction_id']; - $this->transactions[$recurrenceId][$transactionId]['budget_name'] = $mapped[$info['budget_id']]->name ?? ''; + /** @var Preference $preference */ + $preference = Preferences::getForUser($this->user, 'language', config('firefly.default_language', 'en_US')); + $language = $preference->data; + if (is_array($language)) { + $language = 'en_US'; } + $language = (string)$language; + $this->language = $language; } - private function collectNotes(): void + private function processTransactions(array $transactions): array { - $notes = Note::query()->whereIn('noteable_id', $this->ids) - ->whereNotNull('notes.text') - ->where('notes.text', '!=', '') - ->where('noteable_type', Recurrence::class)->get(['notes.noteable_id', 'notes.text'])->toArray() - ; - foreach ($notes as $note) { - $this->notes[(int)$note['noteable_id']] = (string)$note['text']; + $return = []; + $converter = new ExchangeRateConverter(); + foreach ($transactions as $transaction) { + $currencyId = $transaction['transaction_currency_id']; + $pcAmount = null; + $pcForeignAmount = null; + // set the same amount in the primary currency, if both are the same anyway. + if (true === $this->convertToPrimary && $currencyId === (int)$this->primaryCurrency->id) { + $pcAmount = $transaction['amount']; + } + // convert the amount to the primary currency, if it is not the same. + if (true === $this->convertToPrimary && $currencyId !== (int)$this->primaryCurrency->id) { + $pcAmount = $converter->convert($this->currencies[$currencyId], $this->primaryCurrency, today(), $transaction['amount']); + } + if (null !== $transaction['foreign_amount'] && null !== $transaction['foreign_currency_id']) { + $foreignCurrencyId = $transaction['foreign_currency_id']; + if ($foreignCurrencyId !== $this->primaryCurrency->id) { + $pcForeignAmount = $converter->convert($this->currencies[$foreignCurrencyId], $this->primaryCurrency, today(), $transaction['foreign_amount']); + } + } + + $transaction['pc_amount'] = $pcAmount; + $transaction['pc_foreign_amount'] = $pcForeignAmount; + + $sourceId = $transaction['source_id']; + $transaction['source_name'] = $this->accounts[$sourceId]->name; + $transaction['source_iban'] = $this->accounts[$sourceId]->iban; + $transaction['source_type'] = $this->accounts[$sourceId]->accountType->type; + $transaction['source_id'] = (string)$transaction['source_id']; + + $destId = $transaction['destination_id']; + $transaction['destination_name'] = $this->accounts[$destId]->name; + $transaction['destination_iban'] = $this->accounts[$destId]->iban; + $transaction['destination_type'] = $this->accounts[$destId]->accountType->type; + $transaction['destination_id'] = (string)$transaction['destination_id']; + + $transaction['currency_id'] = (string)$currencyId; + $transaction['currency_name'] = $this->currencies[$currencyId]->name; + $transaction['currency_code'] = $this->currencies[$currencyId]->code; + $transaction['currency_symbol'] = $this->currencies[$currencyId]->symbol; + $transaction['currency_decimal_places'] = $this->currencies[$currencyId]->decimal_places; + + $transaction['primary_currency_id'] = (string)$this->primaryCurrency->id; + $transaction['primary_currency_name'] = $this->primaryCurrency->name; + $transaction['primary_currency_code'] = $this->primaryCurrency->code; + $transaction['primary_currency_symbol'] = $this->primaryCurrency->symbol; + $transaction['primary_currency_decimal_places'] = $this->primaryCurrency->decimal_places; + + // $transaction['foreign_currency_id'] = null; + $transaction['foreign_currency_name'] = null; + $transaction['foreign_currency_code'] = null; + $transaction['foreign_currency_symbol'] = null; + $transaction['foreign_currency_decimal_places'] = null; + if (null !== $transaction['foreign_currency_id']) { + $currencyId = $transaction['foreign_currency_id']; + $transaction['foreign_currency_id'] = (string)$currencyId; + $transaction['foreign_currency_name'] = $this->currencies[$currencyId]->name; + $transaction['foreign_currency_code'] = $this->currencies[$currencyId]->code; + $transaction['foreign_currency_symbol'] = $this->currencies[$currencyId]->symbol; + $transaction['foreign_currency_decimal_places'] = $this->currencies[$currencyId]->decimal_places; + } + unset($transaction['transaction_currency_id']); + $return[] = $transaction; } - Log::debug(sprintf('Enrich with %d note(s)', count($this->notes))); + + return $return; } } diff --git a/app/Support/JsonApi/Enrichments/SubscriptionEnrichment.php b/app/Support/JsonApi/Enrichments/SubscriptionEnrichment.php index 06388a80bc..285e0ad37e 100644 --- a/app/Support/JsonApi/Enrichments/SubscriptionEnrichment.php +++ b/app/Support/JsonApi/Enrichments/SubscriptionEnrichment.php @@ -46,20 +46,20 @@ use Illuminate\Support\Facades\Log; class SubscriptionEnrichment implements EnrichmentInterface { - private User $user; - private UserGroup $userGroup; // @phpstan-ignore-line - private Collection $collection; + private BillDateCalculator $calculator; + private Collection $collection; // @phpstan-ignore-line private readonly bool $convertToPrimary; - private ?Carbon $start = null; - private ?Carbon $end = null; - private array $subscriptionIds = []; - private array $objectGroups = []; - private array $mappedObjects = []; - private array $paidDates = []; - private array $notes = []; - private array $payDates = []; + private ?Carbon $end = null; + private array $mappedObjects = []; + private array $notes = []; + private array $objectGroups = []; + private array $paidDates = []; + private array $payDates = []; private readonly TransactionCurrency $primaryCurrency; - private BillDateCalculator $calculator; + private ?Carbon $start = null; + private array $subscriptionIds = []; + private User $user; + private UserGroup $userGroup; public function __construct() { @@ -86,11 +86,11 @@ class SubscriptionEnrichment implements EnrichmentInterface $paidDates = $this->paidDates; $payDates = $this->payDates; $this->collection = $this->collection->map(function (Bill $item) use ($notes, $objectGroups, $paidDates, $payDates) { - $id = (int)$item->id; - $currency = $item->transactionCurrency; - $nem = $this->getNextExpectedMatch($payDates[$id] ?? []); + $id = (int)$item->id; + $currency = $item->transactionCurrency; + $nem = $this->getNextExpectedMatch($payDates[$id] ?? []); - $meta = [ + $meta = [ 'notes' => null, 'object_group_id' => null, 'object_group_title' => null, @@ -101,7 +101,7 @@ class SubscriptionEnrichment implements EnrichmentInterface 'nem' => $nem, 'nem_diff' => $this->getNextExpectedMatchDiff($nem, $payDates[$id] ?? []), ]; - $amounts = [ + $amounts = [ 'amount_min' => Steam::bcround($item->amount_min, $currency->decimal_places), 'amount_max' => Steam::bcround($item->amount_max, $currency->decimal_places), 'average' => Steam::bcround(bcdiv(bcadd($item->amount_min, $item->amount_max), '2'), $currency->decimal_places), @@ -142,7 +142,7 @@ class SubscriptionEnrichment implements EnrichmentInterface return $collection; } - public function enrichSingle(array|Model $model): array|Model + public function enrichSingle(array | Model $model): array | Model { Log::debug(__METHOD__); $collection = new Collection()->push($model); @@ -151,17 +151,14 @@ class SubscriptionEnrichment implements EnrichmentInterface return $collection->first(); } - private function collectNotes(): void + public function setEnd(?Carbon $end): void { - $notes = Note::query()->whereIn('noteable_id', $this->subscriptionIds) - ->whereNotNull('notes.text') - ->where('notes.text', '!=', '') - ->where('noteable_type', Bill::class)->get(['notes.noteable_id', 'notes.text'])->toArray() - ; - foreach ($notes as $note) { - $this->notes[(int)$note['noteable_id']] = (string)$note['text']; - } - Log::debug(sprintf('Enrich with %d note(s)', count($this->notes))); + $this->end = $end; + } + + public function setStart(?Carbon $start): void + { + $this->start = $start; } public function setUser(User $user): void @@ -175,24 +172,49 @@ class SubscriptionEnrichment implements EnrichmentInterface $this->userGroup = $userGroup; } - private function collectSubscriptionIds(): void + /** + * Returns the latest date in the set, or start when set is empty. + */ + protected function lastPaidDate(Bill $subscription, Collection $dates, Carbon $default): Carbon { - /** @var Bill $bill */ - foreach ($this->collection as $bill) { - $this->subscriptionIds[] = (int)$bill->id; + $filtered = $dates->filter(fn(TransactionJournal $journal) => (int)$journal->bill_id === (int)$subscription->id); + Log::debug(sprintf('Filtered down from %d to %d entries for bill #%d.', $dates->count(), $filtered->count(), $subscription->id)); + if (0 === $filtered->count()) { + return $default; } - $this->subscriptionIds = array_unique($this->subscriptionIds); + + $latest = $filtered->first()->date; + + /** @var TransactionJournal $journal */ + foreach ($filtered as $journal) { + if ($journal->date->gte($latest)) { + $latest = $journal->date; + } + } + + return $latest; + } + + private function collectNotes(): void + { + $notes = Note::query()->whereIn('noteable_id', $this->subscriptionIds) + ->whereNotNull('notes.text') + ->where('notes.text', '!=', '') + ->where('noteable_type', Bill::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); + foreach ($notes as $note) { + $this->notes[(int)$note['noteable_id']] = (string)$note['text']; + } + Log::debug(sprintf('Enrich with %d note(s)', count($this->notes))); } private function collectObjectGroups(): void { - $set = DB::table('object_groupables') - ->whereIn('object_groupable_id', $this->subscriptionIds) - ->where('object_groupable_type', Bill::class) - ->get(['object_groupable_id', 'object_group_id']) - ; + $set = DB::table('object_groupables') + ->whereIn('object_groupable_id', $this->subscriptionIds) + ->where('object_groupable_type', Bill::class) + ->get(['object_groupable_id', 'object_group_id']); - $ids = array_unique($set->pluck('object_group_id')->toArray()); + $ids = array_unique($set->pluck('object_group_id')->toArray()); foreach ($set as $entry) { $this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id; @@ -220,13 +242,13 @@ class SubscriptionEnrichment implements EnrichmentInterface // 2023-07-18 this particular date is used to search for the last paid date. // 2023-07-18 the cloned $searchDate is used to grab the correct transactions. /** @var Carbon $start */ - $start = clone $this->start; - $searchStart = clone $start; + $start = clone $this->start; + $searchStart = clone $start; $start->subDay(); /** @var Carbon $end */ - $end = clone $this->end; - $searchEnd = clone $end; + $end = clone $this->end; + $searchEnd = clone $end; // move the search dates to the start of the day. $searchStart->startOfDay(); @@ -235,13 +257,13 @@ class SubscriptionEnrichment implements EnrichmentInterface Log::debug(sprintf('Search parameters are: start: %s, end: %s', $searchStart->format('Y-m-d H:i:s'), $searchEnd->format('Y-m-d H:i:s'))); // Get from database when bills were paid. - $set = $this->user->transactionJournals() - ->whereIn('bill_id', $this->subscriptionIds) - ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->leftJoin('transaction_currencies AS currency', 'currency.id', '=', 'transactions.transaction_currency_id') - ->leftJoin('transaction_currencies AS foreign_currency', 'foreign_currency.id', '=', 'transactions.foreign_currency_id') - ->where('transactions.amount', '>', 0) - ->before($searchEnd)->after($searchStart)->get( + $set = $this->user->transactionJournals() + ->whereIn('bill_id', $this->subscriptionIds) + ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->leftJoin('transaction_currencies AS currency', 'currency.id', '=', 'transactions.transaction_currency_id') + ->leftJoin('transaction_currencies AS foreign_currency', 'foreign_currency.id', '=', 'transactions.foreign_currency_id') + ->where('transactions.amount', '>', 0) + ->before($searchEnd)->after($searchStart)->get( [ 'transaction_journals.id', 'transaction_journals.date', @@ -258,25 +280,24 @@ class SubscriptionEnrichment implements EnrichmentInterface 'transactions.amount', 'transactions.foreign_amount', ] - ) - ; + ); Log::debug(sprintf('Count %d entries in set', $set->count())); // for each bill, do a loop. - $converter = new ExchangeRateConverter(); + $converter = new ExchangeRateConverter(); /** @var Bill $subscription */ foreach ($this->collection as $subscription) { // Grab from array the most recent payment. If none exist, fall back to the start date and pretend *that* was the last paid date. Log::debug(sprintf('Grab last paid date from function, return %s if it comes up with nothing.', $start->format('Y-m-d'))); - $lastPaidDate = $this->lastPaidDate($subscription, $set, $start); + $lastPaidDate = $this->lastPaidDate($subscription, $set, $start); Log::debug(sprintf('Result of lastPaidDate is %s', $lastPaidDate->format('Y-m-d'))); // At this point the "next match" is exactly after the last time the bill was paid. - $result = []; - $filtered = $set->filter(fn (TransactionJournal $journal) => (int)$journal->bill_id === (int)$subscription->id); + $result = []; + $filtered = $set->filter(fn(TransactionJournal $journal) => (int)$journal->bill_id === (int)$subscription->id); foreach ($filtered as $entry) { - $array = [ + $array = [ 'transaction_group_id' => (string)$entry->transaction_group_id, 'transaction_journal_id' => (string)$entry->id, 'date' => $entry->date->toAtomString(), @@ -329,37 +350,47 @@ class SubscriptionEnrichment implements EnrichmentInterface } - public function setStart(?Carbon $start): void + private function collectPayDates(): void { - $this->start = $start; - } + if (!$this->start instanceof Carbon || !$this->end instanceof Carbon) { + Log::debug('Parameters are NULL, set empty array'); - public function setEnd(?Carbon $end): void - { - $this->end = $end; - } - - /** - * Returns the latest date in the set, or start when set is empty. - */ - protected function lastPaidDate(Bill $subscription, Collection $dates, Carbon $default): Carbon - { - $filtered = $dates->filter(fn (TransactionJournal $journal) => (int)$journal->bill_id === (int)$subscription->id); - Log::debug(sprintf('Filtered down from %d to %d entries for bill #%d.', $dates->count(), $filtered->count(), $subscription->id)); - if (0 === $filtered->count()) { - return $default; + return; } - $latest = $filtered->first()->date; - - /** @var TransactionJournal $journal */ - foreach ($filtered as $journal) { - if ($journal->date->gte($latest)) { - $latest = $journal->date; + /** @var Bill $subscription */ + foreach ($this->collection as $subscription) { + $id = (int)$subscription->id; + $lastPaidDate = $this->getLastPaidDate($this->paidDates[$id] ?? []); + $payDates = $this->calculator->getPayDates($this->start, $this->end, $subscription->date, $subscription->repeat_freq, $subscription->skip, $lastPaidDate); + $payDatesFormatted = []; + foreach ($payDates as $string) { + $date = Carbon::createFromFormat('!Y-m-d', $string, config('app.timezone')); + if (!$date instanceof Carbon) { + $date = today(config('app.timezone')); + } + $payDatesFormatted[] = $date->toAtomString(); } + $this->payDates[$id] = $payDatesFormatted; } + } - return $latest; + private function collectSubscriptionIds(): void + { + /** @var Bill $bill */ + foreach ($this->collection as $bill) { + $this->subscriptionIds[] = (int)$bill->id; + } + $this->subscriptionIds = array_unique($this->subscriptionIds); + } + + private function filterPaidDates(array $entries): array + { + return array_map(function (array $entry) { + unset($entry['date_object']); + + return $entry; + }, $entries); } private function getLastPaidDate(array $paidData): ?Carbon @@ -386,40 +417,6 @@ class SubscriptionEnrichment implements EnrichmentInterface return $return; } - private function collectPayDates(): void - { - if (!$this->start instanceof Carbon || !$this->end instanceof Carbon) { - Log::debug('Parameters are NULL, set empty array'); - - return; - } - - /** @var Bill $subscription */ - foreach ($this->collection as $subscription) { - $id = (int)$subscription->id; - $lastPaidDate = $this->getLastPaidDate($this->paidDates[$id] ?? []); - $payDates = $this->calculator->getPayDates($this->start, $this->end, $subscription->date, $subscription->repeat_freq, $subscription->skip, $lastPaidDate); - $payDatesFormatted = []; - foreach ($payDates as $string) { - $date = Carbon::createFromFormat('!Y-m-d', $string, config('app.timezone')); - if (!$date instanceof Carbon) { - $date = today(config('app.timezone')); - } - $payDatesFormatted[] = $date->toAtomString(); - } - $this->payDates[$id] = $payDatesFormatted; - } - } - - private function filterPaidDates(array $entries): array - { - return array_map(function (array $entry) { - unset($entry['date_object']); - - return $entry; - }, $entries); - } - private function getNextExpectedMatch(array $payDates): ?Carbon { // next expected match diff --git a/app/Support/JsonApi/Enrichments/TransactionGroupEnrichment.php b/app/Support/JsonApi/Enrichments/TransactionGroupEnrichment.php index d322331135..014aff8e68 100644 --- a/app/Support/JsonApi/Enrichments/TransactionGroupEnrichment.php +++ b/app/Support/JsonApi/Enrichments/TransactionGroupEnrichment.php @@ -45,17 +45,17 @@ use Override; class TransactionGroupEnrichment implements EnrichmentInterface { - private array $attachmentCount = []; - private Collection $collection; - private readonly array $dateFields; - private array $journalIds = []; - private array $locations = []; - private array $metaData = []; - private array $notes = []; - private array $tags = []; - private User $user; // @phpstan-ignore-line + private array $attachmentCount = []; + private Collection $collection; + private readonly array $dateFields; + private array $journalIds = []; + private array $locations = []; + private array $metaData = []; + private array $notes = []; private readonly TransactionCurrency $primaryCurrency; - private UserGroup $userGroup; // @phpstan-ignore-line + private array $tags = []; // @phpstan-ignore-line + private User $user; + private UserGroup $userGroup; // @phpstan-ignore-line public function __construct() { @@ -63,20 +63,6 @@ class TransactionGroupEnrichment implements EnrichmentInterface $this->primaryCurrency = Amount::getPrimaryCurrency(); } - #[Override] - public function enrichSingle(array|Model $model): array|TransactionGroup - { - Log::debug(__METHOD__); - if (is_array($model)) { - $collection = new Collection()->push($model); - $collection = $this->enrich($collection); - - return $collection->first(); - } - - throw new FireflyException('Cannot enrich single model.'); - } - #[Override] public function enrich(Collection $collection): Collection { @@ -96,119 +82,55 @@ class TransactionGroupEnrichment implements EnrichmentInterface return $this->collection; } - private function collectJournalIds(): void + #[Override] + public function enrichSingle(array | Model $model): array | TransactionGroup { - /** @var array $group */ - foreach ($this->collection as $group) { - foreach ($group['transactions'] as $journal) { - $this->journalIds[] = $journal['transaction_journal_id']; - } + Log::debug(__METHOD__); + if (is_array($model)) { + $collection = new Collection()->push($model); + $collection = $this->enrich($collection); + + return $collection->first(); } - $this->journalIds = array_unique($this->journalIds); + + throw new FireflyException('Cannot enrich single model.'); } - private function collectNotes(): void + public function setUser(User $user): void { - $notes = Note::query()->whereIn('noteable_id', $this->journalIds) - ->whereNotNull('notes.text') - ->where('notes.text', '!=', '') - ->where('noteable_type', TransactionJournal::class)->get(['notes.noteable_id', 'notes.text'])->toArray() - ; - foreach ($notes as $note) { - $this->notes[(int) $note['noteable_id']] = (string) $note['text']; - } - Log::debug(sprintf('Enrich with %d note(s)', count($this->notes))); + $this->user = $user; + $this->userGroup = $user->userGroup; } - private function collectTags(): void + public function setUserGroup(UserGroup $userGroup): void { - $set = Tag::leftJoin('tag_transaction_journal', 'tags.id', '=', 'tag_transaction_journal.tag_id') - ->whereIn('tag_transaction_journal.transaction_journal_id', $this->journalIds) - ->get(['tag_transaction_journal.transaction_journal_id', 'tags.tag'])->toArray() - ; - foreach ($set as $item) { - $journalId = $item['transaction_journal_id']; - $this->tags[$journalId] ??= []; - $this->tags[$journalId][] = $item['tag']; - } - } - - private function collectMetaData(): void - { - $set = TransactionJournalMeta::whereIn('transaction_journal_id', $this->journalIds)->get(['transaction_journal_id', 'name', 'data'])->toArray(); - foreach ($set as $entry) { - $name = $entry['name']; - $data = (string) $entry['data']; - if ('' === $data) { - continue; - } - if (in_array($name, $this->dateFields, true)) { - // Log::debug(sprintf('Meta data for "%s" is a date : "%s"', $name, $data)); - $this->metaData[$entry['transaction_journal_id']][$name] = Carbon::parse($data, config('app.timezone')); - // Log::debug(sprintf('Meta data for "%s" converts to: "%s"', $name, $this->metaData[$entry['transaction_journal_id']][$name]->toW3CString())); - - continue; - } - $this->metaData[(int) $entry['transaction_journal_id']][$name] = $data; - } - } - - private function collectLocations(): void - { - $locations = Location::query()->whereIn('locatable_id', $this->journalIds) - ->where('locatable_type', TransactionJournal::class)->get(['locations.locatable_id', 'locations.latitude', 'locations.longitude', 'locations.zoom_level'])->toArray() - ; - foreach ($locations as $location) { - $this->locations[(int) $location['locatable_id']] - = [ - 'latitude' => (float) $location['latitude'], - 'longitude' => (float) $location['longitude'], - 'zoom_level' => (int) $location['zoom_level'], - ]; - } - Log::debug(sprintf('Enrich with %d locations(s)', count($this->locations))); - } - - private function collectAttachmentCount(): void - { - // select count(id) as nr_of_attachments, attachable_id from attachments - // group by attachable_id - $attachments = Attachment::query() - ->whereIn('attachable_id', $this->journalIds) - ->where('attachable_type', TransactionJournal::class) - ->groupBy('attachable_id') - ->get(['attachable_id', DB::raw('COUNT(id) as nr_of_attachments')]) - ->toArray() - ; - foreach ($attachments as $row) { - $this->attachmentCount[(int) $row['attachable_id']] = (int) $row['nr_of_attachments']; - } + $this->userGroup = $userGroup; } private function appendCollectedData(): void { - $notes = $this->notes; - $tags = $this->tags; - $metaData = $this->metaData; - $locations = $this->locations; - $attachmentCount = $this->attachmentCount; - $primaryCurrency = $this->primaryCurrency; + $notes = $this->notes; + $tags = $this->tags; + $metaData = $this->metaData; + $locations = $this->locations; + $attachmentCount = $this->attachmentCount; + $primaryCurrency = $this->primaryCurrency; $this->collection = $this->collection->map(function (array $item) use ($primaryCurrency, $notes, $tags, $metaData, $locations, $attachmentCount) { foreach ($item['transactions'] as $index => $transaction) { - $journalId = (int) $transaction['transaction_journal_id']; + $journalId = (int)$transaction['transaction_journal_id']; // attach notes if they exist: - $item['transactions'][$index]['notes'] = array_key_exists($journalId, $notes) ? $notes[$journalId] : null; + $item['transactions'][$index]['notes'] = array_key_exists($journalId, $notes) ? $notes[$journalId] : null; // attach tags if they exist: - $item['transactions'][$index]['tags'] = array_key_exists($journalId, $tags) ? $tags[$journalId] : []; + $item['transactions'][$index]['tags'] = array_key_exists($journalId, $tags) ? $tags[$journalId] : []; // attachment count $item['transactions'][$index]['attachment_count'] = array_key_exists($journalId, $attachmentCount) ? $attachmentCount[$journalId] : 0; // default location data - $item['transactions'][$index]['location'] = [ + $item['transactions'][$index]['location'] = [ 'latitude' => null, 'longitude' => null, 'zoom_level' => null, @@ -216,16 +138,16 @@ class TransactionGroupEnrichment implements EnrichmentInterface // primary currency $item['transactions'][$index]['primary_currency'] = [ - 'id' => (string) $primaryCurrency->id, - 'code' => $primaryCurrency->code, - 'name' => $primaryCurrency->name, - 'symbol' => $primaryCurrency->symbol, - 'decimal_places' => $primaryCurrency->decimal_places, + 'id' => (string)$primaryCurrency->id, + 'code' => $primaryCurrency->code, + 'name' => $primaryCurrency->name, + 'symbol' => $primaryCurrency->symbol, + 'decimal_places' => $primaryCurrency->decimal_places, ]; // append meta data - $item['transactions'][$index]['meta'] = []; - $item['transactions'][$index]['meta_date'] = []; + $item['transactions'][$index]['meta'] = []; + $item['transactions'][$index]['meta_date'] = []; if (array_key_exists($journalId, $metaData)) { // loop al meta data: foreach ($metaData[$journalId] as $name => $value) { @@ -248,14 +170,88 @@ class TransactionGroupEnrichment implements EnrichmentInterface }); } - public function setUser(User $user): void + private function collectAttachmentCount(): void { - $this->user = $user; - $this->userGroup = $user->userGroup; + // select count(id) as nr_of_attachments, attachable_id from attachments + // group by attachable_id + $attachments = Attachment::query() + ->whereIn('attachable_id', $this->journalIds) + ->where('attachable_type', TransactionJournal::class) + ->groupBy('attachable_id') + ->get(['attachable_id', DB::raw('COUNT(id) as nr_of_attachments')]) + ->toArray(); + foreach ($attachments as $row) { + $this->attachmentCount[(int)$row['attachable_id']] = (int)$row['nr_of_attachments']; + } } - public function setUserGroup(UserGroup $userGroup): void + private function collectJournalIds(): void { - $this->userGroup = $userGroup; + /** @var array $group */ + foreach ($this->collection as $group) { + foreach ($group['transactions'] as $journal) { + $this->journalIds[] = $journal['transaction_journal_id']; + } + } + $this->journalIds = array_unique($this->journalIds); + } + + private function collectLocations(): void + { + $locations = Location::query()->whereIn('locatable_id', $this->journalIds) + ->where('locatable_type', TransactionJournal::class)->get(['locations.locatable_id', 'locations.latitude', 'locations.longitude', 'locations.zoom_level'])->toArray(); + foreach ($locations as $location) { + $this->locations[(int)$location['locatable_id']] + = [ + 'latitude' => (float)$location['latitude'], + 'longitude' => (float)$location['longitude'], + 'zoom_level' => (int)$location['zoom_level'], + ]; + } + Log::debug(sprintf('Enrich with %d locations(s)', count($this->locations))); + } + + private function collectMetaData(): void + { + $set = TransactionJournalMeta::whereIn('transaction_journal_id', $this->journalIds)->get(['transaction_journal_id', 'name', 'data'])->toArray(); + foreach ($set as $entry) { + $name = $entry['name']; + $data = (string)$entry['data']; + if ('' === $data) { + continue; + } + if (in_array($name, $this->dateFields, true)) { + // Log::debug(sprintf('Meta data for "%s" is a date : "%s"', $name, $data)); + $this->metaData[$entry['transaction_journal_id']][$name] = Carbon::parse($data, config('app.timezone')); + // Log::debug(sprintf('Meta data for "%s" converts to: "%s"', $name, $this->metaData[$entry['transaction_journal_id']][$name]->toW3CString())); + + continue; + } + $this->metaData[(int)$entry['transaction_journal_id']][$name] = $data; + } + } + + private function collectNotes(): void + { + $notes = Note::query()->whereIn('noteable_id', $this->journalIds) + ->whereNotNull('notes.text') + ->where('notes.text', '!=', '') + ->where('noteable_type', TransactionJournal::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); + foreach ($notes as $note) { + $this->notes[(int)$note['noteable_id']] = (string)$note['text']; + } + Log::debug(sprintf('Enrich with %d note(s)', count($this->notes))); + } + + private function collectTags(): void + { + $set = Tag::leftJoin('tag_transaction_journal', 'tags.id', '=', 'tag_transaction_journal.tag_id') + ->whereIn('tag_transaction_journal.transaction_journal_id', $this->journalIds) + ->get(['tag_transaction_journal.transaction_journal_id', 'tags.tag'])->toArray(); + foreach ($set as $item) { + $journalId = $item['transaction_journal_id']; + $this->tags[$journalId] ??= []; + $this->tags[$journalId][] = $item['tag']; + } } } diff --git a/app/Support/JsonApi/Enrichments/WebhookEnrichment.php b/app/Support/JsonApi/Enrichments/WebhookEnrichment.php index 516705892a..0004c291c8 100644 --- a/app/Support/JsonApi/Enrichments/WebhookEnrichment.php +++ b/app/Support/JsonApi/Enrichments/WebhookEnrichment.php @@ -43,16 +43,15 @@ use stdClass; class WebhookEnrichment implements EnrichmentInterface { private Collection $collection; - private User $user; // @phpstan-ignore-line - private UserGroup $userGroup; // @phpstan-ignore-line - private array $ids = []; - private array $deliveries = []; - private array $responses = []; - private array $triggers = []; - - private array $webhookDeliveries = []; - private array $webhookResponses = []; - private array $webhookTriggers = []; + private array $deliveries = []; // @phpstan-ignore-line + private array $ids = []; // @phpstan-ignore-line + private array $responses = []; + private array $triggers = []; + private User $user; + private UserGroup $userGroup; + private array $webhookDeliveries = []; + private array $webhookResponses = []; + private array $webhookTriggers = []; public function enrich(Collection $collection): Collection { @@ -67,7 +66,7 @@ class WebhookEnrichment implements EnrichmentInterface return $this->collection; } - public function enrichSingle(array|Model $model): array|Model + public function enrichSingle(array | Model $model): array | Model { Log::debug(__METHOD__); $collection = new Collection()->push($model); @@ -86,6 +85,20 @@ class WebhookEnrichment implements EnrichmentInterface $this->userGroup = $userGroup; } + private function appendCollectedInfo(): void + { + $this->collection = $this->collection->map(function (Webhook $item) { + $meta = [ + 'deliveries' => $this->webhookDeliveries[$item->id] ?? [], + 'responses' => $this->webhookResponses[$item->id] ?? [], + 'triggers' => $this->webhookTriggers[$item->id] ?? [], + ]; + $item->meta = $meta; + + return $item; + }); + } + private function collectIds(): void { /** @var Webhook $webhook */ @@ -147,18 +160,4 @@ class WebhookEnrichment implements EnrichmentInterface $this->webhookTriggers[$id][] = WebhookTriggerEnum::from($this->triggers[$triggerId])->name; } } - - private function appendCollectedInfo(): void - { - $this->collection = $this->collection->map(function (Webhook $item) { - $meta = [ - 'deliveries' => $this->webhookDeliveries[$item->id] ?? [], - 'responses' => $this->webhookResponses[$item->id] ?? [], - 'triggers' => $this->webhookTriggers[$item->id] ?? [], - ]; - $item->meta = $meta; - - return $item; - }); - } } diff --git a/app/Support/Models/AccountBalanceCalculator.php b/app/Support/Models/AccountBalanceCalculator.php index d2a3572b7d..d9e616f9b9 100644 --- a/app/Support/Models/AccountBalanceCalculator.php +++ b/app/Support/Models/AccountBalanceCalculator.php @@ -62,6 +62,46 @@ class AccountBalanceCalculator $object->optimizedCalculation(new Collection()); } + public static function recalculateForJournal(TransactionJournal $transactionJournal): void + { + Log::debug(__METHOD__); + $object = new self(); + + $set = []; + foreach ($transactionJournal->transactions as $transaction) { + $set[$transaction->account_id] = $transaction->account; + } + $accounts = new Collection()->push(...$set); + $object->optimizedCalculation($accounts, $transactionJournal->date); + } + + private function getLatestBalance(int $accountId, int $currencyId, ?Carbon $notBefore): string + { + if (!$notBefore instanceof Carbon) { + return '0'; + } + Log::debug(sprintf('getLatestBalance: notBefore date is "%s", calculating', $notBefore->format('Y-m-d'))); + $query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->whereNull('transactions.deleted_at') + ->where('transaction_journals.transaction_currency_id', $currencyId) + ->whereNull('transaction_journals.deleted_at') + // this order is the same as GroupCollector + ->orderBy('transaction_journals.date', 'DESC') + ->orderBy('transaction_journals.order', 'ASC') + ->orderBy('transaction_journals.id', 'DESC') + ->orderBy('transaction_journals.description', 'DESC') + ->orderBy('transactions.amount', 'DESC') + ->where('transactions.account_id', $accountId); + $notBefore->startOfDay(); + $query->where('transaction_journals.date', '<', $notBefore); + + $first = $query->first(['transactions.id', 'transactions.balance_dirty', 'transactions.transaction_currency_id', 'transaction_journals.date', 'transactions.account_id', 'transactions.amount', 'transactions.balance_after']); + $balance = (string)($first->balance_after ?? '0'); + Log::debug(sprintf('getLatestBalance: found balance: %s in transaction #%d', $balance, $first->id ?? 0)); + + return $balance; + } + private function optimizedCalculation(Collection $accounts, ?Carbon $notBefore = null): void { Log::debug('start of optimizedCalculation'); @@ -72,15 +112,14 @@ class AccountBalanceCalculator $balances = []; $count = 0; $query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->whereNull('transactions.deleted_at') - ->whereNull('transaction_journals.deleted_at') + ->whereNull('transactions.deleted_at') + ->whereNull('transaction_journals.deleted_at') // this order is the same as GroupCollector, but in the exact reverse. - ->orderBy('transaction_journals.date', 'asc') - ->orderBy('transaction_journals.order', 'desc') - ->orderBy('transaction_journals.id', 'asc') - ->orderBy('transaction_journals.description', 'asc') - ->orderBy('transactions.amount', 'asc') - ; + ->orderBy('transaction_journals.date', 'asc') + ->orderBy('transaction_journals.order', 'desc') + ->orderBy('transaction_journals.id', 'asc') + ->orderBy('transaction_journals.description', 'asc') + ->orderBy('transactions.amount', 'asc'); if ($accounts->count() > 0) { $query->whereIn('transactions.account_id', $accounts->pluck('id')->toArray()); } @@ -89,7 +128,7 @@ class AccountBalanceCalculator $query->where('transaction_journals.date', '>=', $notBefore); } - $set = $query->get(['transactions.id', 'transactions.balance_dirty', 'transactions.transaction_currency_id', 'transaction_journals.date', 'transactions.account_id', 'transactions.amount']); + $set = $query->get(['transactions.id', 'transactions.balance_dirty', 'transactions.transaction_currency_id', 'transaction_journals.date', 'transactions.account_id', 'transactions.amount']); Log::debug(sprintf('Counted %d transaction(s)', $set->count())); // the balance value is an array. @@ -102,8 +141,8 @@ class AccountBalanceCalculator $balances[$entry->account_id][$entry->transaction_currency_id] ??= [$this->getLatestBalance($entry->account_id, $entry->transaction_currency_id, $notBefore), null]; // before and after are easy: - $before = $balances[$entry->account_id][$entry->transaction_currency_id][0]; - $after = bcadd($before, (string)$entry->amount); + $before = $balances[$entry->account_id][$entry->transaction_currency_id][0]; + $after = bcadd($before, (string)$entry->amount); if (true === $entry->balance_dirty || $accounts->count() > 0) { // update the transaction: $entry->balance_before = $before; @@ -123,34 +162,6 @@ class AccountBalanceCalculator $this->storeAccountBalances($balances); } - private function getLatestBalance(int $accountId, int $currencyId, ?Carbon $notBefore): string - { - if (!$notBefore instanceof Carbon) { - return '0'; - } - Log::debug(sprintf('getLatestBalance: notBefore date is "%s", calculating', $notBefore->format('Y-m-d'))); - $query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->whereNull('transactions.deleted_at') - ->where('transaction_journals.transaction_currency_id', $currencyId) - ->whereNull('transaction_journals.deleted_at') - // this order is the same as GroupCollector - ->orderBy('transaction_journals.date', 'DESC') - ->orderBy('transaction_journals.order', 'ASC') - ->orderBy('transaction_journals.id', 'DESC') - ->orderBy('transaction_journals.description', 'DESC') - ->orderBy('transactions.amount', 'DESC') - ->where('transactions.account_id', $accountId) - ; - $notBefore->startOfDay(); - $query->where('transaction_journals.date', '<', $notBefore); - - $first = $query->first(['transactions.id', 'transactions.balance_dirty', 'transactions.transaction_currency_id', 'transaction_journals.date', 'transactions.account_id', 'transactions.amount', 'transactions.balance_after']); - $balance = (string)($first->balance_after ?? '0'); - Log::debug(sprintf('getLatestBalance: found balance: %s in transaction #%d', $balance, $first->id ?? 0)); - - return $balance; - } - private function storeAccountBalances(array $balances): void { /** @@ -196,17 +207,4 @@ class AccountBalanceCalculator } } } - - public static function recalculateForJournal(TransactionJournal $transactionJournal): void - { - Log::debug(__METHOD__); - $object = new self(); - - $set = []; - foreach ($transactionJournal->transactions as $transaction) { - $set[$transaction->account_id] = $transaction->account; - } - $accounts = new Collection()->push(...$set); - $object->optimizedCalculation($accounts, $transactionJournal->date); - } } diff --git a/app/Support/Models/BillDateCalculator.php b/app/Support/Models/BillDateCalculator.php index 3d49c867a3..9f40348777 100644 --- a/app/Support/Models/BillDateCalculator.php +++ b/app/Support/Models/BillDateCalculator.php @@ -49,15 +49,15 @@ class BillDateCalculator Log::debug(sprintf('Dates must be between %s and %s.', $earliest->format('Y-m-d'), $latest->format('Y-m-d'))); Log::debug(sprintf('Bill started on %s, period is "%s", skip is %d, last paid = "%s".', $billStart->format('Y-m-d'), $period, $skip, $lastPaid?->format('Y-m-d'))); - $daysUntilEOM = app('navigation')->daysUntilEndOfMonth($billStart); + $daysUntilEOM = app('navigation')->daysUntilEndOfMonth($billStart); Log::debug(sprintf('For bill start, days until end of month is %d', $daysUntilEOM)); - $set = new Collection(); - $currentStart = clone $earliest; + $set = new Collection(); + $currentStart = clone $earliest; // 2023-06-23 subDay to fix 7655 $currentStart->subDay(); - $loop = 0; + $loop = 0; Log::debug('Start of loop'); while ($currentStart <= $latest) { @@ -107,7 +107,7 @@ class BillDateCalculator // for the next loop, go to end of period, THEN add day. Log::debug('Add one day to nextExpectedMatch/currentStart.'); $nextExpectedMatch->addDay(); - $currentStart = clone $nextExpectedMatch; + $currentStart = clone $nextExpectedMatch; ++$loop; if ($loop > 31) { @@ -117,8 +117,8 @@ class BillDateCalculator } } Log::debug('end of loop'); - $simple = $set->map( // @phpstan-ignore-line - static fn (Carbon $date) => $date->format('Y-m-d') + $simple = $set->map( // @phpstan-ignore-line + static fn(Carbon $date) => $date->format('Y-m-d') ); Log::debug(sprintf('Found %d pay dates', $set->count()), $simple->toArray()); @@ -140,7 +140,7 @@ class BillDateCalculator return $billStartDate; } - $steps = app('navigation')->diffInPeriods($period, $skip, $earliest, $billStartDate); + $steps = app('navigation')->diffInPeriods($period, $skip, $earliest, $billStartDate); if ($steps === $this->diffInMonths) { Log::debug(sprintf('Steps is %d, which is the same as diffInMonths (%d), so we add another 1.', $steps, $this->diffInMonths)); ++$steps; diff --git a/app/Support/Models/ReturnsIntegerIdTrait.php b/app/Support/Models/ReturnsIntegerIdTrait.php index 804b9be384..d8178e07a5 100644 --- a/app/Support/Models/ReturnsIntegerIdTrait.php +++ b/app/Support/Models/ReturnsIntegerIdTrait.php @@ -39,7 +39,7 @@ trait ReturnsIntegerIdTrait protected function id(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } } diff --git a/app/Support/Models/ReturnsIntegerUserIdTrait.php b/app/Support/Models/ReturnsIntegerUserIdTrait.php index a0d2bc79e9..8eca6e943c 100644 --- a/app/Support/Models/ReturnsIntegerUserIdTrait.php +++ b/app/Support/Models/ReturnsIntegerUserIdTrait.php @@ -37,14 +37,14 @@ trait ReturnsIntegerUserIdTrait protected function userGroupId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } protected function userId(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: static fn($value) => (int)$value, ); } } diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php index d88b01ba38..a1e2c256c7 100644 --- a/app/Support/Navigation.php +++ b/app/Support/Navigation.php @@ -77,10 +77,10 @@ class Navigation if (!array_key_exists($repeatFreq, $functionMap)) { Log::error(sprintf( - 'The periodicity %s is unknown. Choose one of available periodicity: %s', - $repeatFreq, - implode(', ', array_keys($functionMap)) - )); + 'The periodicity %s is unknown. Choose one of available periodicity: %s', + $repeatFreq, + implode(', ', array_keys($functionMap)) + )); return $theDate; } @@ -88,30 +88,12 @@ class Navigation return $this->nextDateByInterval($date, $functionMap[$repeatFreq], $skip); } - public function nextDateByInterval(Carbon $epoch, Periodicity $periodicity, int $skipInterval = 0): Carbon - { - try { - return $this->calculator->nextDateByInterval($epoch, $periodicity, $skipInterval); - } catch (IntervalException $exception) { - Log::warning($exception->getMessage(), ['exception' => $exception]); - } catch (Throwable $exception) { - Log::error($exception->getMessage(), ['exception' => $exception]); - } - - Log::debug( - 'Any error occurred to calculate the next date.', - ['date' => $epoch, 'periodicity' => $periodicity->name, 'skipInterval' => $skipInterval] - ); - - return $epoch; - } - public function blockPeriods(Carbon $start, Carbon $end, string $range): array { if ($end < $start) { [$start, $end] = [$end, $start]; } - $periods = []; + $periods = []; // first, 13 periods of [range] $loopCount = 0; $loopDate = clone $end; @@ -159,86 +141,71 @@ class Navigation return $periods; } - public function startOfPeriod(Carbon $theDate, string $repeatFreq): Carbon + public function daysUntilEndOfMonth(Carbon $date): int { - $date = clone $theDate; - // Log::debug(sprintf('Now in startOfPeriod("%s", "%s")', $date->toIso8601String(), $repeatFreq)); - $functionMap = [ - '1D' => 'startOfDay', - 'daily' => 'startOfDay', - '1W' => 'startOfWeek', - 'week' => 'startOfWeek', - 'weekly' => 'startOfWeek', - 'month' => 'startOfMonth', - '1M' => 'startOfMonth', - 'monthly' => 'startOfMonth', - '3M' => 'firstOfQuarter', - 'quarter' => 'firstOfQuarter', - 'quarterly' => 'firstOfQuarter', - 'year' => 'startOfYear', - 'yearly' => 'startOfYear', - '1Y' => 'startOfYear', - 'MTD' => 'startOfMonth', + $endOfMonth = $date->copy()->endOfMonth(); + + return (int)$date->diffInDays($endOfMonth, true); + } + + public function diffInPeriods(string $period, int $skip, Carbon $beginning, Carbon $end): int + { + Log::debug(sprintf( + 'diffInPeriods: %s (skip: %d), between %s and %s.', + $period, + $skip, + $beginning->format('Y-m-d'), + $end->format('Y-m-d') + )); + $map = [ + 'daily' => 'diffInDays', + 'weekly' => 'diffInWeeks', + 'monthly' => 'diffInMonths', + 'quarterly' => 'diffInMonths', + 'half-year' => 'diffInMonths', + 'yearly' => 'diffInYears', ]; + if (!array_key_exists($period, $map)) { + Log::warning(sprintf('No diffInPeriods for period "%s"', $period)); - $parameterMap = [ - 'startOfWeek' => [Carbon::MONDAY], - ]; - - if (array_key_exists($repeatFreq, $functionMap)) { - $function = $functionMap[$repeatFreq]; - // Log::debug(sprintf('Function is ->%s()', $function)); - if (array_key_exists($function, $parameterMap)) { - // Log::debug(sprintf('Parameter map, function becomes ->%s(%s)', $function, implode(', ', $parameterMap[$function]))); - $date->{$function}($parameterMap[$function][0]); // @phpstan-ignore-line - // Log::debug(sprintf('Result is "%s"', $date->toIso8601String())); - - return $date; - } - - $date->{$function}(); // @phpstan-ignore-line - // Log::debug(sprintf('Result is "%s"', $date->toIso8601String())); - - return $date; + return 1; } - if ('half-year' === $repeatFreq || '6M' === $repeatFreq) { - $skipTo = $date->month > 7 ? 6 : 0; - $date->startOfYear()->addMonths($skipTo); - // Log::debug(sprintf('Custom call for "%s": addMonths(%d)', $repeatFreq, $skipTo)); - // Log::debug(sprintf('Result is "%s"', $date->toIso8601String())); + $func = $map[$period]; + // first do the diff + $floatDiff = $beginning->{$func}($end, true); // @phpstan-ignore-line - return $date; + // then correct for quarterly or half-year + if ('quarterly' === $period) { + Log::debug(sprintf('Q: Corrected %f to %f', $floatDiff, $floatDiff / 3)); + $floatDiff /= 3; + } + if ('half-year' === $period) { + Log::debug(sprintf('H: Corrected %f to %f', $floatDiff, $floatDiff / 6)); + $floatDiff /= 6; } - $result = match ($repeatFreq) { - 'last7' => $date->subDays(7)->startOfDay(), - 'last30' => $date->subDays(30)->startOfDay(), - 'last90' => $date->subDays(90)->startOfDay(), - 'last365' => $date->subDays(365)->startOfDay(), - 'MTD' => $date->startOfMonth()->startOfDay(), - 'QTD' => $date->firstOfQuarter()->startOfDay(), - 'YTD' => $date->startOfYear()->startOfDay(), - default => null, - }; - if (null !== $result) { - // Log::debug(sprintf('Result is "%s"', $date->toIso8601String())); + // then do ceil() + $diff = ceil($floatDiff); - return $result; + Log::debug(sprintf('Diff is %f periods (%d rounded up)', $floatDiff, $diff)); + + if ($skip > 0) { + $parameter = $skip + 1; + $diff = ceil($diff / $parameter) * $parameter; + Log::debug(sprintf( + 'diffInPeriods: skip is %d, so param is %d, and diff becomes %d', + $skip, + $parameter, + $diff + )); } - if ('custom' === $repeatFreq) { - // Log::debug(sprintf('Custom, result is "%s"', $date->toIso8601String())); - - return $date; // the date is already at the start. - } - Log::error(sprintf('Cannot do startOfPeriod for $repeat_freq "%s"', $repeatFreq)); - - return $theDate; + return (int)$diff; } public function endOfPeriod(Carbon $end, string $repeatFreq): Carbon { - $currentEnd = clone $end; + $currentEnd = clone $end; // Log::debug(sprintf('Now in endOfPeriod("%s", "%s").', $currentEnd->toIso8601String(), $repeatFreq)); $functionMap = [ @@ -272,11 +239,11 @@ class Navigation Log::debug('Session data available.'); /** @var Carbon $tStart */ - $tStart = session('start', today(config('app.timezone'))->startOfMonth()); + $tStart = session('start', today(config('app.timezone'))->startOfMonth()); /** @var Carbon $tEnd */ $tEnd = session('end', today(config('app.timezone'))->endOfMonth()); - $diffInDays = (int) $tStart->diffInDays($tEnd, true); + $diffInDays = (int)$tStart->diffInDays($tEnd, true); } Log::debug(sprintf('Diff in days is %d', $diffInDays)); $currentEnd->addDays($diffInDays); @@ -286,13 +253,13 @@ class Navigation if ('MTD' === $repeatFreq) { $today = today(); if ($today->isSameMonth($end)) { - return $today->endOfDay(); + return $today->endOfDay()->milli(0); } return $end->endOfMonth(); } - $result = match ($repeatFreq) { + $result = match ($repeatFreq) { 'last7' => $currentEnd->addDays(7)->startOfDay(), 'last30' => $currentEnd->addDays(30)->startOfDay(), 'last90' => $currentEnd->addDays(90)->startOfDay(), @@ -312,19 +279,19 @@ class Navigation return $end; } - $function = $functionMap[$repeatFreq]; + $function = $functionMap[$repeatFreq]; if (array_key_exists($repeatFreq, $modifierMap)) { - $currentEnd->{$function}($modifierMap[$repeatFreq]); // @phpstan-ignore-line + $currentEnd->{$function}($modifierMap[$repeatFreq])->milli(0); // @phpstan-ignore-line if (in_array($repeatFreq, $subDay, true)) { $currentEnd->subDay(); } - $currentEnd->endOfDay(); + $currentEnd->endOfDay()->milli(0); return $currentEnd; } $currentEnd->{$function}(); // @phpstan-ignore-line - $currentEnd->endOfDay(); + $currentEnd->endOfDay()->milli(0); if (in_array($repeatFreq, $subDay, true)) { $currentEnd->subDay(); } @@ -333,68 +300,6 @@ class Navigation return $currentEnd; } - public function daysUntilEndOfMonth(Carbon $date): int - { - $endOfMonth = $date->copy()->endOfMonth(); - - return (int) $date->diffInDays($endOfMonth, true); - } - - public function diffInPeriods(string $period, int $skip, Carbon $beginning, Carbon $end): int - { - Log::debug(sprintf( - 'diffInPeriods: %s (skip: %d), between %s and %s.', - $period, - $skip, - $beginning->format('Y-m-d'), - $end->format('Y-m-d') - )); - $map = [ - 'daily' => 'diffInDays', - 'weekly' => 'diffInWeeks', - 'monthly' => 'diffInMonths', - 'quarterly' => 'diffInMonths', - 'half-year' => 'diffInMonths', - 'yearly' => 'diffInYears', - ]; - if (!array_key_exists($period, $map)) { - Log::warning(sprintf('No diffInPeriods for period "%s"', $period)); - - return 1; - } - $func = $map[$period]; - // first do the diff - $floatDiff = $beginning->{$func}($end, true); // @phpstan-ignore-line - - // then correct for quarterly or half-year - if ('quarterly' === $period) { - Log::debug(sprintf('Q: Corrected %f to %f', $floatDiff, $floatDiff / 3)); - $floatDiff /= 3; - } - if ('half-year' === $period) { - Log::debug(sprintf('H: Corrected %f to %f', $floatDiff, $floatDiff / 6)); - $floatDiff /= 6; - } - - // then do ceil() - $diff = ceil($floatDiff); - - Log::debug(sprintf('Diff is %f periods (%d rounded up)', $floatDiff, $diff)); - - if ($skip > 0) { - $parameter = $skip + 1; - $diff = ceil($diff / $parameter) * $parameter; - Log::debug(sprintf( - 'diffInPeriods: skip is %d, so param is %d, and diff becomes %d', - $skip, - $parameter, - $diff - )); - } - - return (int) $diff; - } - public function endOfX(Carbon $theCurrentEnd, string $repeatFreq, ?Carbon $maxDate): Carbon { $functionMap = [ @@ -414,7 +319,7 @@ class Navigation 'yearly' => 'endOfYear', ]; - $currentEnd = clone $theCurrentEnd; + $currentEnd = clone $theCurrentEnd; if (array_key_exists($repeatFreq, $functionMap)) { $function = $functionMap[$repeatFreq]; @@ -438,7 +343,7 @@ class Navigation if (is_array($range)) { $range = '1M'; } - $range = (string) $range; + $range = (string)$range; if (!$correct) { return $range; } @@ -457,25 +362,25 @@ class Navigation */ public function listOfPeriods(Carbon $start, Carbon $end): array { - $locale = app('steam')->getLocale(); + $locale = app('steam')->getLocale(); // define period to increment $increment = 'addDay'; $format = $this->preferredCarbonFormat($start, $end); - $displayFormat = (string) trans('config.month_and_day_js', [], $locale); + $displayFormat = (string)trans('config.month_and_day_js', [], $locale); $diff = $start->diffInMonths($end, true); // increment by month (for year) if ($diff >= 1.0001 && $diff < 12.001) { $increment = 'addMonth'; - $displayFormat = (string) trans('config.month_js'); + $displayFormat = (string)trans('config.month_js'); } // increment by year (for multi-year) if ($diff >= 12.0001) { $increment = 'addYear'; - $displayFormat = (string) trans('config.year_js'); + $displayFormat = (string)trans('config.year_js'); } - $begin = clone $start; - $entries = []; + $begin = clone $start; + $entries = []; while ($begin < $end) { $formatted = $begin->format($format); $displayed = $begin->isoFormat($displayFormat); @@ -486,6 +391,59 @@ class Navigation return $entries; } + public function nextDateByInterval(Carbon $epoch, Periodicity $periodicity, int $skipInterval = 0): Carbon + { + try { + return $this->calculator->nextDateByInterval($epoch, $periodicity, $skipInterval); + } catch (IntervalException $exception) { + Log::warning($exception->getMessage(), ['exception' => $exception]); + } catch (Throwable $exception) { + Log::error($exception->getMessage(), ['exception' => $exception]); + } + + Log::debug( + 'Any error occurred to calculate the next date.', + ['date' => $epoch, 'periodicity' => $periodicity->name, 'skipInterval' => $skipInterval] + ); + + return $epoch; + } + + public function periodShow(Carbon $theDate, string $repeatFrequency): string + { + $date = clone $theDate; + $formatMap = [ + '1D' => (string)trans('config.specific_day_js'), + 'daily' => (string)trans('config.specific_day_js'), + 'custom' => (string)trans('config.specific_day_js'), + '1W' => (string)trans('config.week_in_year_js'), + 'week' => (string)trans('config.week_in_year_js'), + 'weekly' => (string)trans('config.week_in_year_js'), + '1M' => (string)trans('config.month_js'), + 'month' => (string)trans('config.month_js'), + 'monthly' => (string)trans('config.month_js'), + '1Y' => (string)trans('config.year_js'), + 'year' => (string)trans('config.year_js'), + 'yearly' => (string)trans('config.year_js'), + '6M' => (string)trans('config.half_year_js'), + ]; + + if (array_key_exists($repeatFrequency, $formatMap)) { + return $date->isoFormat($formatMap[$repeatFrequency]); + } + if ('3M' === $repeatFrequency || 'quarter' === $repeatFrequency) { + $quarter = ceil($theDate->month / 3); + + return sprintf('Q%d %d', $quarter, $theDate->year); + } + + // special formatter for quarter of year + Log::error(sprintf('No date formats for frequency "%s"!', $repeatFrequency)); + throw new FireflyException(sprintf('No date formats for frequency "%s"!', $repeatFrequency)); + + return $date->format('Y-m-d'); + } + /** * If the date difference between start and end is less than a month, method returns "Y-m-d". If the difference is * less than a year, method returns "Y-m". If the date difference is larger, method returns "Y". @@ -508,40 +466,6 @@ class Navigation return $format; } - public function periodShow(Carbon $theDate, string $repeatFrequency): string - { - $date = clone $theDate; - $formatMap = [ - '1D' => (string) trans('config.specific_day_js'), - 'daily' => (string) trans('config.specific_day_js'), - 'custom' => (string) trans('config.specific_day_js'), - '1W' => (string) trans('config.week_in_year_js'), - 'week' => (string) trans('config.week_in_year_js'), - 'weekly' => (string) trans('config.week_in_year_js'), - '1M' => (string) trans('config.month_js'), - 'month' => (string) trans('config.month_js'), - 'monthly' => (string) trans('config.month_js'), - '1Y' => (string) trans('config.year_js'), - 'year' => (string) trans('config.year_js'), - 'yearly' => (string) trans('config.year_js'), - '6M' => (string) trans('config.half_year_js'), - ]; - - if (array_key_exists($repeatFrequency, $formatMap)) { - return $date->isoFormat($formatMap[$repeatFrequency]); - } - if ('3M' === $repeatFrequency || 'quarter' === $repeatFrequency) { - $quarter = ceil($theDate->month / 3); - - return sprintf('Q%d %d', $quarter, $theDate->year); - } - - // special formatter for quarter of year - Log::error(sprintf('No date formats for frequency "%s"!', $repeatFrequency)); - - return $date->format('Y-m-d'); - } - /** * Same as preferredCarbonFormat but by string */ @@ -567,14 +491,14 @@ class Navigation $locale = app('steam')->getLocale(); $diff = $start->diffInMonths($end, true); if ($diff >= 1.001 && $diff < 12.001) { - return (string) trans('config.month_js', [], $locale); + return (string)trans('config.month_js', [], $locale); } if ($diff >= 12.001) { - return (string) trans('config.year_js', [], $locale); + return (string)trans('config.year_js', [], $locale); } - return (string) trans('config.month_and_day_js', [], $locale); + return (string)trans('config.month_and_day_js', [], $locale); } /** @@ -631,13 +555,90 @@ class Navigation return '%Y-%m-%d'; } + public function startOfPeriod(Carbon $theDate, string $repeatFreq): Carbon + { + $date = clone $theDate; + // Log::debug(sprintf('Now in startOfPeriod("%s", "%s")', $date->toIso8601String(), $repeatFreq)); + $functionMap = [ + '1D' => 'startOfDay', + 'daily' => 'startOfDay', + '1W' => 'startOfWeek', + 'week' => 'startOfWeek', + 'weekly' => 'startOfWeek', + 'month' => 'startOfMonth', + '1M' => 'startOfMonth', + 'monthly' => 'startOfMonth', + '3M' => 'firstOfQuarter', + 'quarter' => 'firstOfQuarter', + 'quarterly' => 'firstOfQuarter', + 'year' => 'startOfYear', + 'yearly' => 'startOfYear', + '1Y' => 'startOfYear', + 'MTD' => 'startOfMonth', + ]; + + $parameterMap = [ + 'startOfWeek' => [Carbon::MONDAY], + ]; + + if (array_key_exists($repeatFreq, $functionMap)) { + $function = $functionMap[$repeatFreq]; + // Log::debug(sprintf('Function is ->%s()', $function)); + if (array_key_exists($function, $parameterMap)) { + // Log::debug(sprintf('Parameter map, function becomes ->%s(%s)', $function, implode(', ', $parameterMap[$function]))); + $date->{$function}($parameterMap[$function][0]); // @phpstan-ignore-line + // Log::debug(sprintf('Result is "%s"', $date->toIso8601String())); + + return $date; + } + + $date->{$function}(); // @phpstan-ignore-line + // Log::debug(sprintf('Result is "%s"', $date->toIso8601String())); + + return $date; + } + if ('half-year' === $repeatFreq || '6M' === $repeatFreq) { + $skipTo = $date->month > 7 ? 6 : 0; + $date->startOfYear()->addMonths($skipTo); + // Log::debug(sprintf('Custom call for "%s": addMonths(%d)', $repeatFreq, $skipTo)); + // Log::debug(sprintf('Result is "%s"', $date->toIso8601String())); + + return $date; + } + + $result = match ($repeatFreq) { + 'last7' => $date->subDays(7)->startOfDay(), + 'last30' => $date->subDays(30)->startOfDay(), + 'last90' => $date->subDays(90)->startOfDay(), + 'last365' => $date->subDays(365)->startOfDay(), + 'MTD' => $date->startOfMonth()->startOfDay(), + 'QTD' => $date->firstOfQuarter()->startOfDay(), + 'YTD' => $date->startOfYear()->startOfDay(), + default => null, + }; + if (null !== $result) { + // Log::debug(sprintf('Result is "%s"', $date->toIso8601String())); + + return $result; + } + + if ('custom' === $repeatFreq) { + // Log::debug(sprintf('Custom, result is "%s"', $date->toIso8601String())); + + return $date; // the date is already at the start. + } + Log::error(sprintf('Cannot do startOfPeriod for $repeat_freq "%s"', $repeatFreq)); + + return $theDate; + } + /** * @throws FireflyException */ public function subtractPeriod(Carbon $theDate, string $repeatFreq, ?int $subtract = null): Carbon { $subtract ??= 1; - $date = clone $theDate; + $date = clone $theDate; // 1D 1W 1M 3M 6M 1Y $functionMap = [ '1D' => 'subDays', @@ -676,11 +677,11 @@ class Navigation // this is then subtracted from $theDate (* $subtract). if ('custom' === $repeatFreq) { /** @var Carbon $tStart */ - $tStart = session('start', today(config('app.timezone'))->startOfMonth()); + $tStart = session('start', today(config('app.timezone'))->startOfMonth()); /** @var Carbon $tEnd */ $tEnd = session('end', today(config('app.timezone'))->endOfMonth()); - $diffInDays = (int) $tStart->diffInDays($tEnd, true); + $diffInDays = (int)$tStart->diffInDays($tEnd, true); $date->subDays($diffInDays * $subtract); return $date; @@ -770,7 +771,7 @@ class Navigation return $fiscalHelper->endOfFiscalYear($end); } - $list = [ + $list = [ 'last7', 'last30', 'last90', diff --git a/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php b/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php index 3f50036ab1..d71872971c 100644 --- a/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php +++ b/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php @@ -39,12 +39,98 @@ use Spatie\Period\Precision; trait RecalculatesAvailableBudgetsTrait { + private function calculateAmount(AvailableBudget $availableBudget): void + { + $repository = app(BudgetLimitRepositoryInterface::class); + $repository->setUser($availableBudget->user); + $newAmount = '0'; + $abPeriod = Period::make($availableBudget->start_date, $availableBudget->end_date, Precision::DAY()); + Log::debug( + sprintf( + 'Now at AB #%d, ("%s" to "%s")', + $availableBudget->id, + $availableBudget->start_date->format('Y-m-d'), + $availableBudget->end_date->format('Y-m-d') + ) + ); + // have to recalculate everything just in case. + $set = $repository->getAllBudgetLimitsByCurrency($availableBudget->transactionCurrency, $availableBudget->start_date, $availableBudget->end_date); + Log::debug(sprintf('Found %d interesting budget limit(s).', $set->count())); + + /** @var BudgetLimit $budgetLimit */ + foreach ($set as $budgetLimit) { + Log::debug( + sprintf( + 'Found interesting budget limit #%d ("%s" to "%s")', + $budgetLimit->id, + $budgetLimit->start_date->format('Y-m-d'), + $budgetLimit->end_date->format('Y-m-d') + ) + ); + // overlap in days: + $limitPeriod = Period::make( + $budgetLimit->start_date, + $budgetLimit->end_date, + precision : Precision::DAY(), + boundaries: Boundaries::EXCLUDE_NONE() + ); + // if both equal each other, amount from this BL must be added to the AB + if ($limitPeriod->equals($abPeriod)) { + Log::debug('This budget limit is equal to the available budget period.'); + $newAmount = bcadd($newAmount, (string)$budgetLimit->amount); + } + // if budget limit period is inside AB period, it can be added in full. + if (!$limitPeriod->equals($abPeriod) && $abPeriod->contains($limitPeriod)) { + Log::debug('This budget limit is smaller than the available budget period.'); + $newAmount = bcadd($newAmount, (string)$budgetLimit->amount); + } + if (!$limitPeriod->equals($abPeriod) && !$abPeriod->contains($limitPeriod) && $abPeriod->overlapsWith($limitPeriod)) { + Log::debug('This budget limit is something else entirely!'); + $overlap = $abPeriod->overlap($limitPeriod); + if ($overlap instanceof Period) { + $length = $overlap->length(); + $daily = bcmul($this->getDailyAmount($budgetLimit), (string)$length); + $newAmount = bcadd($newAmount, $daily); + } + } + } + if (0 === bccomp('0', $newAmount)) { + Log::debug('New amount is zero, deleting AB.'); + $availableBudget->delete(); + + return; + } + Log::debug(sprintf('Concluded new amount for this AB must be %s', $newAmount)); + $availableBudget->amount = app('steam')->bcround($newAmount, $availableBudget->transactionCurrency->decimal_places); + $availableBudget->save(); + } + + private function getDailyAmount(BudgetLimit $budgetLimit): string + { + if (0 === $budgetLimit->id) { + return '0'; + } + $limitPeriod = Period::make( + $budgetLimit->start_date, + $budgetLimit->end_date, + precision : Precision::DAY(), + boundaries: Boundaries::EXCLUDE_NONE() + ); + $days = $limitPeriod->length(); + $amount = bcdiv($budgetLimit->amount, (string)$days, 12); + Log::debug( + sprintf('Total amount for budget limit #%d is %s. Nr. of days is %d. Amount per day is %s', $budgetLimit->id, $budgetLimit->amount, $days, $amount) + ); + + return $amount; + } + private function updateAvailableBudget(BudgetLimit $budgetLimit): void { Log::debug(sprintf('Now in updateAvailableBudget(limit #%d)', $budgetLimit->id)); /** @var null|Budget $budget */ - $budget = Budget::find($budgetLimit->budget_id); + $budget = Budget::find($budgetLimit->budget_id); if (null === $budget) { Log::warning('Budget is null, probably deleted, find deleted version.'); @@ -59,7 +145,7 @@ trait RecalculatesAvailableBudgetsTrait } /** @var null|User $user */ - $user = $budget->user; + $user = $budget->user; // sanity check. It happens when the budget has been deleted so the original user is unknown. if (null === $user) { @@ -75,7 +161,7 @@ trait RecalculatesAvailableBudgetsTrait // all have to be created or updated. try { $viewRange = app('preferences')->getForUser($user, 'viewRange', '1M')->data; - } catch (ContainerExceptionInterface|NotFoundExceptionInterface $e) { + } catch (ContainerExceptionInterface | NotFoundExceptionInterface $e) { Log::error($e->getMessage()); $viewRange = '1M'; } @@ -83,20 +169,20 @@ trait RecalculatesAvailableBudgetsTrait if (null === $viewRange || is_array($viewRange)) { $viewRange = '1M'; } - $viewRange = (string) $viewRange; + $viewRange = (string)$viewRange; - $start = app('navigation')->startOfPeriod($budgetLimit->start_date, $viewRange); - $end = app('navigation')->startOfPeriod($budgetLimit->end_date, $viewRange); - $end = app('navigation')->endOfPeriod($end, $viewRange); + $start = app('navigation')->startOfPeriod($budgetLimit->start_date, $viewRange); + $end = app('navigation')->startOfPeriod($budgetLimit->end_date, $viewRange); + $end = app('navigation')->endOfPeriod($end, $viewRange); // limit period in total is: $limitPeriod = Period::make($start, $end, precision: Precision::DAY(), boundaries: Boundaries::EXCLUDE_NONE()); Log::debug(sprintf('Limit period is from %s to %s', $start->format('Y-m-d'), $end->format('Y-m-d'))); // from the start until the end of the budget limit, need to loop! - $current = clone $start; + $current = clone $start; while ($current <= $end) { - $currentEnd = app('navigation')->endOfPeriod($current, $viewRange); + $currentEnd = app('navigation')->endOfPeriod($current, $viewRange); // create or find AB for this particular period, and set the amount accordingly. /** @var null|AvailableBudget $availableBudget */ @@ -111,7 +197,7 @@ trait RecalculatesAvailableBudgetsTrait // if not exists: $currentPeriod = Period::make($current, $currentEnd, precision: Precision::DAY(), boundaries: Boundaries::EXCLUDE_NONE()); $daily = $this->getDailyAmount($budgetLimit); - $amount = bcmul($daily, (string) $currentPeriod->length(), 12); + $amount = bcmul($daily, (string)$currentPeriod->length(), 12); // no need to calculate if period is equal. if ($currentPeriod->equals($limitPeriod)) { @@ -141,93 +227,7 @@ trait RecalculatesAvailableBudgetsTrait } // prep for next loop - $current = app('navigation')->addPeriod($current, $viewRange, 0); + $current = app('navigation')->addPeriod($current, $viewRange, 0); } } - - private function calculateAmount(AvailableBudget $availableBudget): void - { - $repository = app(BudgetLimitRepositoryInterface::class); - $repository->setUser($availableBudget->user); - $newAmount = '0'; - $abPeriod = Period::make($availableBudget->start_date, $availableBudget->end_date, Precision::DAY()); - Log::debug( - sprintf( - 'Now at AB #%d, ("%s" to "%s")', - $availableBudget->id, - $availableBudget->start_date->format('Y-m-d'), - $availableBudget->end_date->format('Y-m-d') - ) - ); - // have to recalculate everything just in case. - $set = $repository->getAllBudgetLimitsByCurrency($availableBudget->transactionCurrency, $availableBudget->start_date, $availableBudget->end_date); - Log::debug(sprintf('Found %d interesting budget limit(s).', $set->count())); - - /** @var BudgetLimit $budgetLimit */ - foreach ($set as $budgetLimit) { - Log::debug( - sprintf( - 'Found interesting budget limit #%d ("%s" to "%s")', - $budgetLimit->id, - $budgetLimit->start_date->format('Y-m-d'), - $budgetLimit->end_date->format('Y-m-d') - ) - ); - // overlap in days: - $limitPeriod = Period::make( - $budgetLimit->start_date, - $budgetLimit->end_date, - precision : Precision::DAY(), - boundaries: Boundaries::EXCLUDE_NONE() - ); - // if both equal each other, amount from this BL must be added to the AB - if ($limitPeriod->equals($abPeriod)) { - Log::debug('This budget limit is equal to the available budget period.'); - $newAmount = bcadd($newAmount, (string) $budgetLimit->amount); - } - // if budget limit period is inside AB period, it can be added in full. - if (!$limitPeriod->equals($abPeriod) && $abPeriod->contains($limitPeriod)) { - Log::debug('This budget limit is smaller than the available budget period.'); - $newAmount = bcadd($newAmount, (string) $budgetLimit->amount); - } - if (!$limitPeriod->equals($abPeriod) && !$abPeriod->contains($limitPeriod) && $abPeriod->overlapsWith($limitPeriod)) { - Log::debug('This budget limit is something else entirely!'); - $overlap = $abPeriod->overlap($limitPeriod); - if ($overlap instanceof Period) { - $length = $overlap->length(); - $daily = bcmul($this->getDailyAmount($budgetLimit), (string) $length); - $newAmount = bcadd($newAmount, $daily); - } - } - } - if (0 === bccomp('0', $newAmount)) { - Log::debug('New amount is zero, deleting AB.'); - $availableBudget->delete(); - - return; - } - Log::debug(sprintf('Concluded new amount for this AB must be %s', $newAmount)); - $availableBudget->amount = app('steam')->bcround($newAmount, $availableBudget->transactionCurrency->decimal_places); - $availableBudget->save(); - } - - private function getDailyAmount(BudgetLimit $budgetLimit): string - { - if (0 === $budgetLimit->id) { - return '0'; - } - $limitPeriod = Period::make( - $budgetLimit->start_date, - $budgetLimit->end_date, - precision : Precision::DAY(), - boundaries: Boundaries::EXCLUDE_NONE() - ); - $days = $limitPeriod->length(); - $amount = bcdiv($budgetLimit->amount, (string) $days, 12); - Log::debug( - sprintf('Total amount for budget limit #%d is %s. Nr. of days is %d. Amount per day is %s', $budgetLimit->id, $budgetLimit->amount, $days, $amount) - ); - - return $amount; - } } diff --git a/app/Support/ParseDateString.php b/app/Support/ParseDateString.php index f2ab22a672..bd91585f8b 100644 --- a/app/Support/ParseDateString.php +++ b/app/Support/ParseDateString.php @@ -29,7 +29,6 @@ use Carbon\CarbonInterface; use Carbon\Exceptions\InvalidFormatException; use FireflyIII\Exceptions\FireflyException; use Illuminate\Support\Facades\Log; - use function Safe\preg_match; /** @@ -79,15 +78,15 @@ class ParseDateString public function parseDate(string $date): Carbon { Log::debug(sprintf('parseDate("%s")', $date)); - $date = strtolower($date); + $date = strtolower($date); // parse keywords: if (in_array($date, $this->keywords, true)) { return $this->parseKeyword($date); } // if regex for YYYY-MM-DD: - $pattern = '/^(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01])$/'; - $result = preg_match($pattern, $date); + $pattern = '/^(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01])$/'; + $result = preg_match($pattern, $date); if (0 !== $result) { return $this->parseDefaultDate($date); } @@ -114,99 +113,13 @@ class ParseDateString return new Carbon('1984-09-17'); } // maybe a year, nothing else? - if (4 === strlen($date) && is_numeric($date) && (int) $date > 1000 && (int) $date <= 3000) { + if (4 === strlen($date) && is_numeric($date) && (int)$date > 1000 && (int)$date <= 3000) { return new Carbon(sprintf('%d-01-01', $date)); } throw new FireflyException(sprintf('[d] Not a recognised date format: "%s"', $date)); } - protected function parseKeyword(string $keyword): Carbon - { - $today = today(config('app.timezone'))->startOfDay(); - - return match ($keyword) { - default => $today, - 'yesterday' => $today->subDay(), - 'tomorrow' => $today->addDay(), - 'start of this week' => $today->startOfWeek(CarbonInterface::MONDAY), - 'end of this week' => $today->endOfWeek(CarbonInterface::SUNDAY), - 'start of this month' => $today->startOfMonth(), - 'end of this month' => $today->endOfMonth(), - 'start of this quarter' => $today->startOfQuarter(), - 'end of this quarter' => $today->endOfQuarter(), - 'start of this year' => $today->startOfYear(), - 'end of this year' => $today->endOfYear(), - }; - } - - protected function parseDefaultDate(string $date): Carbon - { - $result = false; - - try { - $result = Carbon::createFromFormat('Y-m-d', $date); - } catch (InvalidFormatException $e) { - Log::error(sprintf('parseDefaultDate("%s") ran into an error, but dont mind: %s', $date, $e->getMessage())); - } - if (false === $result) { - return today(config('app.timezone'))->startOfDay(); - } - - return $result; - } - - protected function parseRelativeDate(string $date): Carbon - { - Log::debug(sprintf('Now in parseRelativeDate("%s")', $date)); - $parts = explode(' ', $date); - $today = today(config('app.timezone'))->startOfDay(); - $functions = [ - [ - 'd' => 'subDays', - 'w' => 'subWeeks', - 'm' => 'subMonths', - 'q' => 'subQuarters', - 'y' => 'subYears', - ], - [ - 'd' => 'addDays', - 'w' => 'addWeeks', - 'm' => 'addMonths', - 'q' => 'addQuarters', - 'y' => 'addYears', - ], - ]; - - foreach ($parts as $part) { - Log::debug(sprintf('Now parsing part "%s"', $part)); - $part = trim($part); - - // verify if correct - $pattern = '/[+-]\d+[wqmdy]/'; - $result = preg_match($pattern, $part); - if (0 === $result) { - Log::error(sprintf('Part "%s" does not match regular expression. Will be skipped.', $part)); - - continue; - } - $direction = str_starts_with($part, '+') ? 1 : 0; - $period = $part[strlen($part) - 1]; - $number = (int) substr($part, 1, -1); - if (!array_key_exists($period, $functions[$direction])) { - Log::error(sprintf('No method for direction %d and period "%s".', $direction, $period)); - - continue; - } - $func = $functions[$direction][$period]; - Log::debug(sprintf('Will now do %s(%d) on %s', $func, $number, $today->format('Y-m-d'))); - $today->{$func}($number); // @phpstan-ignore-line - Log::debug(sprintf('Resulting date is %s', $today->format('Y-m-d'))); - } - - return $today; - } - public function parseRange(string $date): array { // several types of range can be submitted @@ -269,16 +182,34 @@ class ParseDateString return false; } - /** - * format of string is xxxx-xx-DD - */ - protected function parseDayRange(string $date): array + protected function isDayYearRange(string $date): bool { - $parts = explode('-', $date); + // if regex for YYYY-xx-DD: + $pattern = '/^(19|20)\d\d-xx-(0[1-9]|[12]\d|3[01])$/'; + $result = preg_match($pattern, $date); + if (0 !== $result) { + Log::debug(sprintf('"%s" is a day/year range.', $date)); - return [ - 'day' => $parts[2], - ]; + return true; + } + Log::debug(sprintf('"%s" is not a day/year range.', $date)); + + return false; + } + + protected function isMonthDayRange(string $date): bool + { + // if regex for xxxx-MM-DD: + $pattern = '/^xxxx-(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01])$/'; + $result = preg_match($pattern, $date); + if (0 !== $result) { + Log::debug(sprintf('"%s" is a month/day range.', $date)); + + return true; + } + Log::debug(sprintf('"%s" is not a month/day range.', $date)); + + return false; } protected function isMonthRange(string $date): bool @@ -296,17 +227,19 @@ class ParseDateString return false; } - /** - * format of string is xxxx-MM-xx - */ - protected function parseMonthRange(string $date): array + protected function isMonthYearRange(string $date): bool { - Log::debug(sprintf('parseMonthRange: Parsed "%s".', $date)); - $parts = explode('-', $date); + // if regex for YYYY-MM-xx: + $pattern = '/^(19|20)\d\d-(0[1-9]|1[012])-xx$/'; + $result = preg_match($pattern, $date); + if (0 !== $result) { + Log::debug(sprintf('"%s" is a month/year range.', $date)); - return [ - 'month' => $parts[1], - ]; + return true; + } + Log::debug(sprintf('"%s" is not a month/year range.', $date)); + + return false; } protected function isYearRange(string $date): bool @@ -324,6 +257,131 @@ class ParseDateString return false; } + /** + * format of string is xxxx-xx-DD + */ + protected function parseDayRange(string $date): array + { + $parts = explode('-', $date); + + return [ + 'day' => $parts[2], + ]; + } + + protected function parseDefaultDate(string $date): Carbon + { + $result = false; + + try { + $result = Carbon::createFromFormat('Y-m-d', $date); + } catch (InvalidFormatException $e) { + Log::error(sprintf('parseDefaultDate("%s") ran into an error, but dont mind: %s', $date, $e->getMessage())); + } + if (false === $result) { + return today(config('app.timezone'))->startOfDay(); + } + + return $result; + } + + protected function parseKeyword(string $keyword): Carbon + { + $today = today(config('app.timezone'))->startOfDay(); + + return match ($keyword) { + default => $today, + 'yesterday' => $today->subDay(), + 'tomorrow' => $today->addDay(), + 'start of this week' => $today->startOfWeek(CarbonInterface::MONDAY), + 'end of this week' => $today->endOfWeek(CarbonInterface::SUNDAY), + 'start of this month' => $today->startOfMonth(), + 'end of this month' => $today->endOfMonth(), + 'start of this quarter' => $today->startOfQuarter(), + 'end of this quarter' => $today->endOfQuarter(), + 'start of this year' => $today->startOfYear(), + 'end of this year' => $today->endOfYear(), + }; + } + + /** + * format of string is xxxx-MM-xx + */ + protected function parseMonthRange(string $date): array + { + Log::debug(sprintf('parseMonthRange: Parsed "%s".', $date)); + $parts = explode('-', $date); + + return [ + 'month' => $parts[1], + ]; + } + + /** + * format of string is YYYY-MM-xx + */ + protected function parseMonthYearRange(string $date): array + { + Log::debug(sprintf('parseMonthYearRange: Parsed "%s".', $date)); + $parts = explode('-', $date); + + return [ + 'year' => $parts[0], + 'month' => $parts[1], + ]; + } + + protected function parseRelativeDate(string $date): Carbon + { + Log::debug(sprintf('Now in parseRelativeDate("%s")', $date)); + $parts = explode(' ', $date); + $today = today(config('app.timezone'))->startOfDay(); + $functions = [ + [ + 'd' => 'subDays', + 'w' => 'subWeeks', + 'm' => 'subMonths', + 'q' => 'subQuarters', + 'y' => 'subYears', + ], + [ + 'd' => 'addDays', + 'w' => 'addWeeks', + 'm' => 'addMonths', + 'q' => 'addQuarters', + 'y' => 'addYears', + ], + ]; + + foreach ($parts as $part) { + Log::debug(sprintf('Now parsing part "%s"', $part)); + $part = trim($part); + + // verify if correct + $pattern = '/[+-]\d+[wqmdy]/'; + $result = preg_match($pattern, $part); + if (0 === $result) { + Log::error(sprintf('Part "%s" does not match regular expression. Will be skipped.', $part)); + + continue; + } + $direction = str_starts_with($part, '+') ? 1 : 0; + $period = $part[strlen($part) - 1]; + $number = (int)substr($part, 1, -1); + if (!array_key_exists($period, $functions[$direction])) { + Log::error(sprintf('No method for direction %d and period "%s".', $direction, $period)); + + continue; + } + $func = $functions[$direction][$period]; + Log::debug(sprintf('Will now do %s(%d) on %s', $func, $number, $today->format('Y-m-d'))); + $today->{$func}($number); // @phpstan-ignore-line + Log::debug(sprintf('Resulting date is %s', $today->format('Y-m-d'))); + } + + return $today; + } + /** * format of string is YYYY-xx-xx */ @@ -337,50 +395,6 @@ class ParseDateString ]; } - protected function isMonthDayRange(string $date): bool - { - // if regex for xxxx-MM-DD: - $pattern = '/^xxxx-(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01])$/'; - $result = preg_match($pattern, $date); - if (0 !== $result) { - Log::debug(sprintf('"%s" is a month/day range.', $date)); - - return true; - } - Log::debug(sprintf('"%s" is not a month/day range.', $date)); - - return false; - } - - /** - * format of string is xxxx-MM-DD - */ - private function parseMonthDayRange(string $date): array - { - Log::debug(sprintf('parseMonthDayRange: Parsed "%s".', $date)); - $parts = explode('-', $date); - - return [ - 'month' => $parts[1], - 'day' => $parts[2], - ]; - } - - protected function isDayYearRange(string $date): bool - { - // if regex for YYYY-xx-DD: - $pattern = '/^(19|20)\d\d-xx-(0[1-9]|[12]\d|3[01])$/'; - $result = preg_match($pattern, $date); - if (0 !== $result) { - Log::debug(sprintf('"%s" is a day/year range.', $date)); - - return true; - } - Log::debug(sprintf('"%s" is not a day/year range.', $date)); - - return false; - } - /** * format of string is YYYY-xx-DD */ @@ -395,32 +409,17 @@ class ParseDateString ]; } - protected function isMonthYearRange(string $date): bool - { - // if regex for YYYY-MM-xx: - $pattern = '/^(19|20)\d\d-(0[1-9]|1[012])-xx$/'; - $result = preg_match($pattern, $date); - if (0 !== $result) { - Log::debug(sprintf('"%s" is a month/year range.', $date)); - - return true; - } - Log::debug(sprintf('"%s" is not a month/year range.', $date)); - - return false; - } - /** - * format of string is YYYY-MM-xx + * format of string is xxxx-MM-DD */ - protected function parseMonthYearRange(string $date): array + private function parseMonthDayRange(string $date): array { - Log::debug(sprintf('parseMonthYearRange: Parsed "%s".', $date)); + Log::debug(sprintf('parseMonthDayRange: Parsed "%s".', $date)); $parts = explode('-', $date); return [ - 'year' => $parts[0], 'month' => $parts[1], + 'day' => $parts[2], ]; } } diff --git a/app/Support/Preferences.php b/app/Support/Preferences.php index 4a068cae06..f8fc423cc7 100644 --- a/app/Support/Preferences.php +++ b/app/Support/Preferences.php @@ -48,73 +48,19 @@ class Preferences } return Preference::where('user_id', $user->id) - ->where('name', '!=', 'currencyPreference') - ->where(function (Builder $q) use ($user): void { - $q->whereNull('user_group_id'); - $q->orWhere('user_group_id', $user->user_group_id); - }) - ->get() - ; + ->where('name', '!=', 'currencyPreference') + ->where(function (Builder $q) use ($user): void { + $q->whereNull('user_group_id'); + $q->orWhere('user_group_id', $user->user_group_id); + }) + ->get(); } - public function get(string $name, array|bool|int|string|null $default = null): ?Preference + public function beginsWith(User $user, string $search): Collection { - /** @var null|User $user */ - $user = auth()->user(); - if (null === $user) { - $preference = new Preference(); - $preference->data = $default; + $value = sprintf('%s%%', $search); - return $preference; - } - - return $this->getForUser($user, $name, $default); - } - - public function getForUser(User $user, string $name, array|bool|int|string|null $default = null): ?Preference - { - // Log::debug(sprintf('getForUser(#%d, "%s")', $user->id, $name)); - // don't care about user group ID, except for some specific preferences. - $userGroupId = $this->getUserGroupId($user, $name); - $query = Preference::where('user_id', $user->id)->where('name', $name); - if (null !== $userGroupId) { - Log::debug('Include user group ID in query'); - $query->where('user_group_id', $userGroupId); - } - - $preference = $query->first(['id', 'user_id', 'user_group_id', 'name', 'data', 'updated_at', 'created_at']); - - if (null !== $preference && null === $preference->data) { - $preference->delete(); - $preference = null; - Log::debug('Removed empty preference.'); - } - - if (null !== $preference) { - // Log::debug(sprintf('Found preference #%d for user #%d: %s', $preference->id, $user->id, $name)); - - return $preference; - } - // no preference found and default is null: - if (null === $default) { - Log::debug('Return NULL, create no preference.'); - - // return NULL - return null; - } - - return $this->setForUser($user, $name, $default); - } - - private function getUserGroupId(User $user, string $preferenceName): ?int - { - $groupId = null; - $items = config('firefly.admin_specific_prefs') ?? []; - if (in_array($preferenceName, $items, true)) { - return (int) $user->user_group_id; - } - - return $groupId; + return Preference::where('user_id', $user->id)->whereLike('name', $value)->get(); } public function delete(string $name): bool @@ -128,58 +74,6 @@ class Preferences return true; } - public function forget(User $user, string $name): void - { - $key = sprintf('preference%s%s', $user->id, $name); - Cache::forget($key); - Cache::put($key, '', 5); - } - - public function setForUser(User $user, string $name, array|bool|int|string|null $value): Preference - { - $fullName = sprintf('preference%s%s', $user->id, $name); - $userGroupId = $this->getUserGroupId($user, $name); - $userGroupId = 0 === (int) $userGroupId ? null : (int) $userGroupId; - - Cache::forget($fullName); - - $query = Preference::where('user_id', $user->id)->where('name', $name); - if (null !== $userGroupId) { - Log::debug('Include user group ID in query'); - $query->where('user_group_id', $userGroupId); - } - - $preference = $query->first(['id', 'user_id', 'user_group_id', 'name', 'data', 'updated_at', 'created_at']); - - if (null !== $preference && null === $value) { - $preference->delete(); - - return new Preference(); - } - if (null === $value) { - return new Preference(); - } - if (null === $preference) { - $preference = new Preference(); - $preference->user_id = (int) $user->id; - $preference->user_group_id = $userGroupId; - $preference->name = $name; - - } - $preference->data = $value; - $preference->save(); - Cache::forever($fullName, $preference); - - return $preference; - } - - public function beginsWith(User $user, string $search): Collection - { - $value = sprintf('%s%%', $search); - - return Preference::where('user_id', $user->id)->whereLike('name', $value)->get(); - } - /** * Find by name, has no user ID in it, because the method is called from an unauthenticated route any way. */ @@ -188,17 +82,37 @@ class Preferences return Preference::where('name', $name)->get(); } + public function forget(User $user, string $name): void + { + $key = sprintf('preference%s%s', $user->id, $name); + Cache::forget($key); + Cache::put($key, '', 5); + } + + public function get(string $name, array | bool | int | string | null $default = null): ?Preference + { + /** @var null|User $user */ + $user = auth()->user(); + if (null === $user) { + $preference = new Preference(); + $preference->data = $default; + + return $preference; + } + + return $this->getForUser($user, $name, $default); + } + public function getArrayForUser(User $user, array $list): array { $result = []; $preferences = Preference::where('user_id', $user->id) - ->where(function (Builder $q) use ($user): void { - $q->whereNull('user_group_id'); - $q->orWhere('user_group_id', $user->user_group_id); - }) - ->whereIn('name', $list) - ->get(['id', 'name', 'data']) - ; + ->where(function (Builder $q) use ($user): void { + $q->whereNull('user_group_id'); + $q->orWhere('user_group_id', $user->user_group_id); + }) + ->whereIn('name', $list) + ->get(['id', 'name', 'data']); /** @var Preference $preference */ foreach ($preferences as $preference) { @@ -240,7 +154,7 @@ class Preferences return $result; } - public function getEncryptedForUser(User $user, string $name, array|bool|int|string|null $default = null): ?Preference + public function getEncryptedForUser(User $user, string $name, array | bool | int | string | null $default = null): ?Preference { $result = $this->getForUser($user, $name, $default); if ('' === $result->data) { @@ -265,7 +179,42 @@ class Preferences return $result; } - public function getFresh(string $name, array|bool|int|string|null $default = null): ?Preference + public function getForUser(User $user, string $name, array | bool | int | string | null $default = null): ?Preference + { + // Log::debug(sprintf('getForUser(#%d, "%s")', $user->id, $name)); + // don't care about user group ID, except for some specific preferences. + $userGroupId = $this->getUserGroupId($user, $name); + $query = Preference::where('user_id', $user->id)->where('name', $name); + if (null !== $userGroupId) { + Log::debug('Include user group ID in query'); + $query->where('user_group_id', $userGroupId); + } + + $preference = $query->first(['id', 'user_id', 'user_group_id', 'name', 'data', 'updated_at', 'created_at']); + + if (null !== $preference && null === $preference->data) { + $preference->delete(); + $preference = null; + Log::debug('Removed empty preference.'); + } + + if (null !== $preference) { + // Log::debug(sprintf('Found preference #%d for user #%d: %s', $preference->id, $user->id, $name)); + + return $preference; + } + // no preference found and default is null: + if (null === $default) { + Log::debug('Return NULL, create no preference.'); + + // return NULL + return null; + } + + return $this->setForUser($user, $name, $default); + } + + public function getFresh(string $name, array | bool | int | string | null $default = null): ?Preference { /** @var null|User $user */ $user = auth()->user(); @@ -284,8 +233,8 @@ class Preferences */ public function lastActivity(): string { - $instance = PreferencesSingleton::getInstance(); - $pref = $instance->getPreference('last_activity'); + $instance = PreferencesSingleton::getInstance(); + $pref = $instance->getPreference('last_activity'); if (null !== $pref) { // Log::debug(sprintf('Found last activity in singleton: %s', $pref)); return $pref; @@ -299,7 +248,7 @@ class Preferences if (is_array($lastActivity)) { $lastActivity = implode(',', $lastActivity); } - $setting = hash('sha256', (string) $lastActivity); + $setting = hash('sha256', (string)$lastActivity); $instance->setPreference('last_activity', $setting); return $setting; @@ -313,7 +262,7 @@ class Preferences Session::forget('first'); } - public function set(string $name, array|bool|int|string|null $value): Preference + public function set(string $name, array | bool | int | string | null $value): Preference { /** @var null|User $user */ $user = auth()->user(); @@ -341,4 +290,53 @@ class Preferences return $this->set($name, $encrypted); } + + public function setForUser(User $user, string $name, array | bool | int | string | null $value): Preference + { + $fullName = sprintf('preference%s%s', $user->id, $name); + $userGroupId = $this->getUserGroupId($user, $name); + $userGroupId = 0 === (int)$userGroupId ? null : (int)$userGroupId; + + Cache::forget($fullName); + + $query = Preference::where('user_id', $user->id)->where('name', $name); + if (null !== $userGroupId) { + Log::debug('Include user group ID in query'); + $query->where('user_group_id', $userGroupId); + } + + $preference = $query->first(['id', 'user_id', 'user_group_id', 'name', 'data', 'updated_at', 'created_at']); + + if (null !== $preference && null === $value) { + $preference->delete(); + + return new Preference(); + } + if (null === $value) { + return new Preference(); + } + if (null === $preference) { + $preference = new Preference(); + $preference->user_id = (int)$user->id; + $preference->user_group_id = $userGroupId; + $preference->name = $name; + + } + $preference->data = $value; + $preference->save(); + Cache::forever($fullName, $preference); + + return $preference; + } + + private function getUserGroupId(User $user, string $preferenceName): ?int + { + $groupId = null; + $items = config('firefly.admin_specific_prefs') ?? []; + if (in_array($preferenceName, $items, true)) { + return (int)$user->user_group_id; + } + + return $groupId; + } } diff --git a/app/Support/Report/Budget/BudgetReportGenerator.php b/app/Support/Report/Budget/BudgetReportGenerator.php index c478e3cce4..b859847748 100644 --- a/app/Support/Report/Budget/BudgetReportGenerator.php +++ b/app/Support/Report/Budget/BudgetReportGenerator.php @@ -76,7 +76,7 @@ class BudgetReportGenerator /** @var Account $account */ foreach ($this->accounts as $account) { - $accountId = $account->id; + $accountId = $account->id; $this->report[$accountId] ??= [ 'name' => $account->name, 'id' => $account->id, @@ -91,43 +91,6 @@ class BudgetReportGenerator } } - /** - * Process each row of expenses collected for the "Account per budget" partial - */ - private function processExpenses(array $expenses): void - { - foreach ($expenses['budgets'] as $budget) { - $this->processBudgetExpenses($expenses, $budget); - } - } - - /** - * Process each set of transactions for each row of expenses. - */ - private function processBudgetExpenses(array $expenses, array $budget): void - { - $budgetId = (int) $budget['id']; - $currencyId = (int) $expenses['currency_id']; - foreach ($budget['transaction_journals'] as $journal) { - $sourceAccountId = $journal['source_account_id']; - - $this->report[$sourceAccountId]['currencies'][$currencyId] - ??= [ - 'currency_id' => $expenses['currency_id'], - 'currency_symbol' => $expenses['currency_symbol'], - 'currency_name' => $expenses['currency_name'], - 'currency_decimal_places' => $expenses['currency_decimal_places'], - 'budgets' => [], - ]; - - $this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId] - ??= '0'; - - $this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId] - = bcadd($this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId], (string) $journal['amount']); - } - } - /** * Generates the data necessary to create the card that displays * the budget overview in the general report. @@ -144,175 +107,6 @@ class BudgetReportGenerator $this->percentageReport(); } - /** - * Start the budgets block on the default report by processing every budget. - */ - private function generalBudgetReport(): void - { - $budgetList = $this->repository->getBudgets(); - - /** @var Budget $budget */ - foreach ($budgetList as $budget) { - $this->processBudget($budget); - } - } - - /** - * Process expenses etc. for a single budget for the budgets block on the default report. - */ - private function processBudget(Budget $budget): void - { - $budgetId = $budget->id; - $this->report['budgets'][$budgetId] ??= [ - 'budget_id' => $budgetId, - 'budget_name' => $budget->name, - 'no_budget' => false, - 'budget_limits' => [], - ]; - - // get all budget limits for budget in period: - $limits = $this->blRepository->getBudgetLimits($budget, $this->start, $this->end); - - /** @var BudgetLimit $limit */ - foreach ($limits as $limit) { - $this->processLimit($budget, $limit); - } - } - - /** - * Process a single budget limit for the budgets block on the default report. - */ - private function processLimit(Budget $budget, BudgetLimit $limit): void - { - $budgetId = $budget->id; - $limitId = $limit->id; - $limitCurrency = $limit->transactionCurrency ?? $this->currency; - $currencyId = $limitCurrency->id; - $expenses = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, $this->accounts, new Collection()->push($budget)); - $spent = $expenses[$currencyId]['sum'] ?? '0'; - $left = -1 === bccomp(bcadd($limit->amount, $spent), '0') ? '0' : bcadd($limit->amount, $spent); - $overspent = 1 === bccomp(bcmul($spent, '-1'), $limit->amount) ? bcadd($spent, $limit->amount) : '0'; - - $this->report['budgets'][$budgetId]['budget_limits'][$limitId] ??= [ - 'budget_limit_id' => $limitId, - 'start_date' => $limit->start_date, - 'end_date' => $limit->end_date, - 'budgeted' => $limit->amount, - 'budgeted_pct' => '0', - 'spent' => $spent, - 'spent_pct' => '0', - 'left' => $left, - 'overspent' => $overspent, - 'currency_id' => $currencyId, - 'currency_code' => $limitCurrency->code, - 'currency_name' => $limitCurrency->name, - 'currency_symbol' => $limitCurrency->symbol, - 'currency_decimal_places' => $limitCurrency->decimal_places, - ]; - - // make sum information: - $this->report['sums'][$currencyId] - ??= [ - 'budgeted' => '0', - 'spent' => '0', - 'left' => '0', - 'overspent' => '0', - 'currency_id' => $currencyId, - 'currency_code' => $limitCurrency->code, - 'currency_name' => $limitCurrency->name, - 'currency_symbol' => $limitCurrency->symbol, - 'currency_decimal_places' => $limitCurrency->decimal_places, - ]; - $this->report['sums'][$currencyId]['budgeted'] = bcadd((string) $this->report['sums'][$currencyId]['budgeted'], $limit->amount); - $this->report['sums'][$currencyId]['spent'] = bcadd((string) $this->report['sums'][$currencyId]['spent'], $spent); - $this->report['sums'][$currencyId]['left'] = bcadd((string) $this->report['sums'][$currencyId]['left'], bcadd($limit->amount, $spent)); - $this->report['sums'][$currencyId]['overspent'] = bcadd((string) $this->report['sums'][$currencyId]['overspent'], $overspent); - } - - /** - * Calculate the expenses for transactions without a budget. Part of the "budgets" block of the default report. - */ - private function noBudgetReport(): void - { - // add no budget info. - $this->report['budgets'][0] = [ - 'budget_id' => null, - 'budget_name' => null, - 'no_budget' => true, - 'budget_limits' => [], - ]; - - $noBudget = $this->nbRepository->sumExpenses($this->start, $this->end, $this->accounts); - foreach ($noBudget as $noBudgetEntry) { - // currency information: - $nbCurrencyId = (int) ($noBudgetEntry['currency_id'] ?? $this->currency->id); - $nbCurrencyCode = $noBudgetEntry['currency_code'] ?? $this->currency->code; - $nbCurrencyName = $noBudgetEntry['currency_name'] ?? $this->currency->name; - $nbCurrencySymbol = $noBudgetEntry['currency_symbol'] ?? $this->currency->symbol; - $nbCurrencyDp = $noBudgetEntry['currency_decimal_places'] ?? $this->currency->decimal_places; - - $this->report['budgets'][0]['budget_limits'][] = [ - 'budget_limit_id' => null, - 'start_date' => $this->start, - 'end_date' => $this->end, - 'budgeted' => '0', - 'budgeted_pct' => '0', - 'spent' => $noBudgetEntry['sum'], - 'spent_pct' => '0', - 'left' => '0', - 'overspent' => '0', - 'currency_id' => $nbCurrencyId, - 'currency_code' => $nbCurrencyCode, - 'currency_name' => $nbCurrencyName, - 'currency_symbol' => $nbCurrencySymbol, - 'currency_decimal_places' => $nbCurrencyDp, - ]; - $this->report['sums'][$nbCurrencyId]['spent'] = bcadd($this->report['sums'][$nbCurrencyId]['spent'] ?? '0', (string) $noBudgetEntry['sum']); - // append currency info because it may be missing: - $this->report['sums'][$nbCurrencyId]['currency_id'] = $nbCurrencyId; - $this->report['sums'][$nbCurrencyId]['currency_code'] = $nbCurrencyCode; - $this->report['sums'][$nbCurrencyId]['currency_name'] = $nbCurrencyName; - $this->report['sums'][$nbCurrencyId]['currency_symbol'] = $nbCurrencySymbol; - $this->report['sums'][$nbCurrencyId]['currency_decimal_places'] = $nbCurrencyDp; - - // append other sums because they might be missing: - $this->report['sums'][$nbCurrencyId]['overspent'] ??= '0'; - $this->report['sums'][$nbCurrencyId]['left'] ??= '0'; - $this->report['sums'][$nbCurrencyId]['budgeted'] ??= '0'; - } - } - - /** - * Calculate the percentages for each budget. Part of the "budgets" block on the default report. - */ - private function percentageReport(): void - { - // make percentages based on total amount. - foreach ($this->report['budgets'] as $budgetId => $data) { - foreach ($data['budget_limits'] as $limitId => $entry) { - $budgetId = (int) $budgetId; - $limitId = (int) $limitId; - $currencyId = (int) $entry['currency_id']; - $spent = $entry['spent']; - $totalSpent = $this->report['sums'][$currencyId]['spent'] ?? '0'; - $spentPct = '0'; - $budgeted = $entry['budgeted']; - $totalBudgeted = $this->report['sums'][$currencyId]['budgeted'] ?? '0'; - $budgetedPct = '0'; - - if (0 !== bccomp((string) $spent, '0') && 0 !== bccomp($totalSpent, '0')) { - $spentPct = round((float) bcmul(bcdiv((string) $spent, $totalSpent), '100')); - } - if (0 !== bccomp((string) $budgeted, '0') && 0 !== bccomp($totalBudgeted, '0')) { - $budgetedPct = round((float) bcmul(bcdiv((string) $budgeted, $totalBudgeted), '100')); - } - $this->report['sums'][$currencyId]['budgeted'] ??= '0'; - $this->report['budgets'][$budgetId]['budget_limits'][$limitId]['spent_pct'] = $spentPct; - $this->report['budgets'][$budgetId]['budget_limits'][$limitId]['budgeted_pct'] = $budgetedPct; - } - } - } - public function getReport(): array { return $this->report; @@ -349,4 +143,210 @@ class BudgetReportGenerator $this->nbRepository->setUser($user); $this->currency = app('amount')->getPrimaryCurrencyByUserGroup($user->userGroup); } + + /** + * Start the budgets block on the default report by processing every budget. + */ + private function generalBudgetReport(): void + { + $budgetList = $this->repository->getBudgets(); + + /** @var Budget $budget */ + foreach ($budgetList as $budget) { + $this->processBudget($budget); + } + } + + /** + * Calculate the expenses for transactions without a budget. Part of the "budgets" block of the default report. + */ + private function noBudgetReport(): void + { + // add no budget info. + $this->report['budgets'][0] = [ + 'budget_id' => null, + 'budget_name' => null, + 'no_budget' => true, + 'budget_limits' => [], + ]; + + $noBudget = $this->nbRepository->sumExpenses($this->start, $this->end, $this->accounts); + foreach ($noBudget as $noBudgetEntry) { + // currency information: + $nbCurrencyId = (int)($noBudgetEntry['currency_id'] ?? $this->currency->id); + $nbCurrencyCode = $noBudgetEntry['currency_code'] ?? $this->currency->code; + $nbCurrencyName = $noBudgetEntry['currency_name'] ?? $this->currency->name; + $nbCurrencySymbol = $noBudgetEntry['currency_symbol'] ?? $this->currency->symbol; + $nbCurrencyDp = $noBudgetEntry['currency_decimal_places'] ?? $this->currency->decimal_places; + + $this->report['budgets'][0]['budget_limits'][] = [ + 'budget_limit_id' => null, + 'start_date' => $this->start, + 'end_date' => $this->end, + 'budgeted' => '0', + 'budgeted_pct' => '0', + 'spent' => $noBudgetEntry['sum'], + 'spent_pct' => '0', + 'left' => '0', + 'overspent' => '0', + 'currency_id' => $nbCurrencyId, + 'currency_code' => $nbCurrencyCode, + 'currency_name' => $nbCurrencyName, + 'currency_symbol' => $nbCurrencySymbol, + 'currency_decimal_places' => $nbCurrencyDp, + ]; + $this->report['sums'][$nbCurrencyId]['spent'] = bcadd($this->report['sums'][$nbCurrencyId]['spent'] ?? '0', (string)$noBudgetEntry['sum']); + // append currency info because it may be missing: + $this->report['sums'][$nbCurrencyId]['currency_id'] = $nbCurrencyId; + $this->report['sums'][$nbCurrencyId]['currency_code'] = $nbCurrencyCode; + $this->report['sums'][$nbCurrencyId]['currency_name'] = $nbCurrencyName; + $this->report['sums'][$nbCurrencyId]['currency_symbol'] = $nbCurrencySymbol; + $this->report['sums'][$nbCurrencyId]['currency_decimal_places'] = $nbCurrencyDp; + + // append other sums because they might be missing: + $this->report['sums'][$nbCurrencyId]['overspent'] ??= '0'; + $this->report['sums'][$nbCurrencyId]['left'] ??= '0'; + $this->report['sums'][$nbCurrencyId]['budgeted'] ??= '0'; + } + } + + /** + * Calculate the percentages for each budget. Part of the "budgets" block on the default report. + */ + private function percentageReport(): void + { + // make percentages based on total amount. + foreach ($this->report['budgets'] as $budgetId => $data) { + foreach ($data['budget_limits'] as $limitId => $entry) { + $budgetId = (int)$budgetId; + $limitId = (int)$limitId; + $currencyId = (int)$entry['currency_id']; + $spent = $entry['spent']; + $totalSpent = $this->report['sums'][$currencyId]['spent'] ?? '0'; + $spentPct = '0'; + $budgeted = $entry['budgeted']; + $totalBudgeted = $this->report['sums'][$currencyId]['budgeted'] ?? '0'; + $budgetedPct = '0'; + + if (0 !== bccomp((string)$spent, '0') && 0 !== bccomp($totalSpent, '0')) { + $spentPct = round((float)bcmul(bcdiv((string)$spent, $totalSpent), '100')); + } + if (0 !== bccomp((string)$budgeted, '0') && 0 !== bccomp($totalBudgeted, '0')) { + $budgetedPct = round((float)bcmul(bcdiv((string)$budgeted, $totalBudgeted), '100')); + } + $this->report['sums'][$currencyId]['budgeted'] ??= '0'; + $this->report['budgets'][$budgetId]['budget_limits'][$limitId]['spent_pct'] = $spentPct; + $this->report['budgets'][$budgetId]['budget_limits'][$limitId]['budgeted_pct'] = $budgetedPct; + } + } + } + + /** + * Process expenses etc. for a single budget for the budgets block on the default report. + */ + private function processBudget(Budget $budget): void + { + $budgetId = $budget->id; + $this->report['budgets'][$budgetId] ??= [ + 'budget_id' => $budgetId, + 'budget_name' => $budget->name, + 'no_budget' => false, + 'budget_limits' => [], + ]; + + // get all budget limits for budget in period: + $limits = $this->blRepository->getBudgetLimits($budget, $this->start, $this->end); + + /** @var BudgetLimit $limit */ + foreach ($limits as $limit) { + $this->processLimit($budget, $limit); + } + } + + /** + * Process each set of transactions for each row of expenses. + */ + private function processBudgetExpenses(array $expenses, array $budget): void + { + $budgetId = (int)$budget['id']; + $currencyId = (int)$expenses['currency_id']; + foreach ($budget['transaction_journals'] as $journal) { + $sourceAccountId = $journal['source_account_id']; + + $this->report[$sourceAccountId]['currencies'][$currencyId] + ??= [ + 'currency_id' => $expenses['currency_id'], + 'currency_symbol' => $expenses['currency_symbol'], + 'currency_name' => $expenses['currency_name'], + 'currency_decimal_places' => $expenses['currency_decimal_places'], + 'budgets' => [], + ]; + + $this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId] + ??= '0'; + + $this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId] + = bcadd($this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId], (string)$journal['amount']); + } + } + + /** + * Process each row of expenses collected for the "Account per budget" partial + */ + private function processExpenses(array $expenses): void + { + foreach ($expenses['budgets'] as $budget) { + $this->processBudgetExpenses($expenses, $budget); + } + } + + /** + * Process a single budget limit for the budgets block on the default report. + */ + private function processLimit(Budget $budget, BudgetLimit $limit): void + { + $budgetId = $budget->id; + $limitId = $limit->id; + $limitCurrency = $limit->transactionCurrency ?? $this->currency; + $currencyId = $limitCurrency->id; + $expenses = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, $this->accounts, new Collection()->push($budget)); + $spent = $expenses[$currencyId]['sum'] ?? '0'; + $left = -1 === bccomp(bcadd($limit->amount, $spent), '0') ? '0' : bcadd($limit->amount, $spent); + $overspent = 1 === bccomp(bcmul($spent, '-1'), $limit->amount) ? bcadd($spent, $limit->amount) : '0'; + + $this->report['budgets'][$budgetId]['budget_limits'][$limitId] ??= [ + 'budget_limit_id' => $limitId, + 'start_date' => $limit->start_date, + 'end_date' => $limit->end_date, + 'budgeted' => $limit->amount, + 'budgeted_pct' => '0', + 'spent' => $spent, + 'spent_pct' => '0', + 'left' => $left, + 'overspent' => $overspent, + 'currency_id' => $currencyId, + 'currency_code' => $limitCurrency->code, + 'currency_name' => $limitCurrency->name, + 'currency_symbol' => $limitCurrency->symbol, + 'currency_decimal_places' => $limitCurrency->decimal_places, + ]; + + // make sum information: + $this->report['sums'][$currencyId] + ??= [ + 'budgeted' => '0', + 'spent' => '0', + 'left' => '0', + 'overspent' => '0', + 'currency_id' => $currencyId, + 'currency_code' => $limitCurrency->code, + 'currency_name' => $limitCurrency->name, + 'currency_symbol' => $limitCurrency->symbol, + 'currency_decimal_places' => $limitCurrency->decimal_places, + ]; + $this->report['sums'][$currencyId]['budgeted'] = bcadd((string)$this->report['sums'][$currencyId]['budgeted'], $limit->amount); + $this->report['sums'][$currencyId]['spent'] = bcadd((string)$this->report['sums'][$currencyId]['spent'], $spent); + $this->report['sums'][$currencyId]['left'] = bcadd((string)$this->report['sums'][$currencyId]['left'], bcadd($limit->amount, $spent)); + $this->report['sums'][$currencyId]['overspent'] = bcadd((string)$this->report['sums'][$currencyId]['overspent'], $overspent); + } } diff --git a/app/Support/Report/Category/CategoryReportGenerator.php b/app/Support/Report/Category/CategoryReportGenerator.php index 91f1470bb8..8f800d411d 100644 --- a/app/Support/Report/Category/CategoryReportGenerator.php +++ b/app/Support/Report/Category/CategoryReportGenerator.php @@ -62,17 +62,17 @@ class CategoryReportGenerator */ public function operations(): void { - $earnedWith = $this->opsRepository->listIncome($this->start, $this->end, $this->accounts); - $spentWith = $this->opsRepository->listExpenses($this->start, $this->end, $this->accounts); + $earnedWith = $this->opsRepository->listIncome($this->start, $this->end, $this->accounts); + $spentWith = $this->opsRepository->listExpenses($this->start, $this->end, $this->accounts); // also transferred out and transferred into these accounts in this category: $transferredIn = $this->opsRepository->listTransferredIn($this->start, $this->end, $this->accounts); $transferredOut = $this->opsRepository->listTransferredOut($this->start, $this->end, $this->accounts); - $earnedWithout = $this->noCatRepository->listIncome($this->start, $this->end, $this->accounts); - $spentWithout = $this->noCatRepository->listExpenses($this->start, $this->end, $this->accounts); + $earnedWithout = $this->noCatRepository->listIncome($this->start, $this->end, $this->accounts); + $spentWithout = $this->noCatRepository->listExpenses($this->start, $this->end, $this->accounts); - $this->report = [ + $this->report = [ 'categories' => [], 'sums' => [], ]; @@ -83,17 +83,69 @@ class CategoryReportGenerator } } - /** - * Process one of the spent arrays from the operations method. - */ - private function processOpsArray(array $data): void + public function setAccounts(Collection $accounts): void { - /** - * @var int $currencyId - * @var array $currencyRow - */ - foreach ($data as $currencyId => $currencyRow) { - $this->processCurrencyArray($currencyId, $currencyRow); + $this->accounts = $accounts; + } + + public function setEnd(Carbon $end): void + { + $this->end = $end; + } + + public function setStart(Carbon $start): void + { + $this->start = $start; + } + + public function setUser(User $user): void + { + $this->noCatRepository->setUser($user); + $this->opsRepository->setUser($user); + } + + private function processCategoryRow(int $currencyId, array $currencyRow, int $categoryId, array $categoryRow): void + { + $key = sprintf('%s-%s', $currencyId, $categoryId); + $this->report['categories'][$key] ??= [ + 'id' => $categoryId, + 'title' => $categoryRow['name'], + 'currency_id' => $currencyRow['currency_id'], + 'currency_symbol' => $currencyRow['currency_symbol'], + 'currency_name' => $currencyRow['currency_name'], + 'currency_code' => $currencyRow['currency_code'], + 'currency_decimal_places' => $currencyRow['currency_decimal_places'], + 'spent' => '0', + 'earned' => '0', + 'sum' => '0', + ]; + // loop journals: + foreach ($categoryRow['transaction_journals'] as $journal) { + // sum of sums + $this->report['sums'][$currencyId]['sum'] = bcadd((string)$this->report['sums'][$currencyId]['sum'], (string)$journal['amount']); + // sum of spent: + $this->report['sums'][$currencyId]['spent'] = -1 === bccomp((string)$journal['amount'], '0') ? bcadd( + (string)$this->report['sums'][$currencyId]['spent'], + (string)$journal['amount'] + ) : $this->report['sums'][$currencyId]['spent']; + // sum of earned + $this->report['sums'][$currencyId]['earned'] = 1 === bccomp((string)$journal['amount'], '0') ? bcadd( + (string)$this->report['sums'][$currencyId]['earned'], + (string)$journal['amount'] + ) : $this->report['sums'][$currencyId]['earned']; + + // sum of category + $this->report['categories'][$key]['sum'] = bcadd((string)$this->report['categories'][$key]['sum'], (string)$journal['amount']); + // total spent in category + $this->report['categories'][$key]['spent'] = -1 === bccomp((string)$journal['amount'], '0') ? bcadd( + (string)$this->report['categories'][$key]['spent'], + (string)$journal['amount'] + ) : $this->report['categories'][$key]['spent']; + // total earned in category + $this->report['categories'][$key]['earned'] = 1 === bccomp((string)$journal['amount'], '0') ? bcadd( + (string)$this->report['categories'][$key]['earned'], + (string)$journal['amount'] + ) : $this->report['categories'][$key]['earned']; } } @@ -119,69 +171,17 @@ class CategoryReportGenerator } } - private function processCategoryRow(int $currencyId, array $currencyRow, int $categoryId, array $categoryRow): void + /** + * Process one of the spent arrays from the operations method. + */ + private function processOpsArray(array $data): void { - $key = sprintf('%s-%s', $currencyId, $categoryId); - $this->report['categories'][$key] ??= [ - 'id' => $categoryId, - 'title' => $categoryRow['name'], - 'currency_id' => $currencyRow['currency_id'], - 'currency_symbol' => $currencyRow['currency_symbol'], - 'currency_name' => $currencyRow['currency_name'], - 'currency_code' => $currencyRow['currency_code'], - 'currency_decimal_places' => $currencyRow['currency_decimal_places'], - 'spent' => '0', - 'earned' => '0', - 'sum' => '0', - ]; - // loop journals: - foreach ($categoryRow['transaction_journals'] as $journal) { - // sum of sums - $this->report['sums'][$currencyId]['sum'] = bcadd((string) $this->report['sums'][$currencyId]['sum'], (string) $journal['amount']); - // sum of spent: - $this->report['sums'][$currencyId]['spent'] = -1 === bccomp((string) $journal['amount'], '0') ? bcadd( - (string) $this->report['sums'][$currencyId]['spent'], - (string) $journal['amount'] - ) : $this->report['sums'][$currencyId]['spent']; - // sum of earned - $this->report['sums'][$currencyId]['earned'] = 1 === bccomp((string) $journal['amount'], '0') ? bcadd( - (string) $this->report['sums'][$currencyId]['earned'], - (string) $journal['amount'] - ) : $this->report['sums'][$currencyId]['earned']; - - // sum of category - $this->report['categories'][$key]['sum'] = bcadd((string) $this->report['categories'][$key]['sum'], (string) $journal['amount']); - // total spent in category - $this->report['categories'][$key]['spent'] = -1 === bccomp((string) $journal['amount'], '0') ? bcadd( - (string) $this->report['categories'][$key]['spent'], - (string) $journal['amount'] - ) : $this->report['categories'][$key]['spent']; - // total earned in category - $this->report['categories'][$key]['earned'] = 1 === bccomp((string) $journal['amount'], '0') ? bcadd( - (string) $this->report['categories'][$key]['earned'], - (string) $journal['amount'] - ) : $this->report['categories'][$key]['earned']; + /** + * @var int $currencyId + * @var array $currencyRow + */ + foreach ($data as $currencyId => $currencyRow) { + $this->processCurrencyArray($currencyId, $currencyRow); } } - - public function setAccounts(Collection $accounts): void - { - $this->accounts = $accounts; - } - - public function setEnd(Carbon $end): void - { - $this->end = $end; - } - - public function setStart(Carbon $start): void - { - $this->start = $start; - } - - public function setUser(User $user): void - { - $this->noCatRepository->setUser($user); - $this->opsRepository->setUser($user); - } } diff --git a/app/Support/Report/Summarizer/TransactionSummarizer.php b/app/Support/Report/Summarizer/TransactionSummarizer.php index aea25a5663..84e0ec3231 100644 --- a/app/Support/Report/Summarizer/TransactionSummarizer.php +++ b/app/Support/Report/Summarizer/TransactionSummarizer.php @@ -43,26 +43,19 @@ class TransactionSummarizer } } - public function setUser(User $user): void - { - $this->user = $user; - $this->default = Amount::getPrimaryCurrencyByUserGroup($user->userGroup); - $this->convertToPrimary = Amount::convertToPrimary($user); - } - public function groupByCurrencyId(array $journals, string $method = 'negative', bool $includeForeign = true): array { Log::debug(sprintf('Now in groupByCurrencyId([%d journals], "%s", %s)', count($journals), $method, var_export($includeForeign, true))); $array = []; foreach ($journals as $journal) { - $field = 'amount'; + $field = 'amount'; // grab default currency information. - $currencyId = (int) $journal['currency_id']; - $currencyName = $journal['currency_name']; - $currencySymbol = $journal['currency_symbol']; - $currencyCode = $journal['currency_code']; - $currencyDecimalPlaces = $journal['currency_decimal_places']; + $currencyId = (int)$journal['currency_id']; + $currencyName = $journal['currency_name']; + $currencySymbol = $journal['currency_symbol']; + $currencyCode = $journal['currency_code']; + $currencyDecimalPlaces = $journal['currency_decimal_places']; // prepare foreign currency info: $foreignCurrencyId = 0; @@ -74,8 +67,8 @@ class TransactionSummarizer if ($this->convertToPrimary) { // Log::debug('convertToPrimary is true.'); // if convert to primary currency, use the primary currency amount yes or no? - $usePrimary = $this->default->id !== (int) $journal['currency_id']; - $useForeign = $this->default->id === (int) $journal['foreign_currency_id']; + $usePrimary = $this->default->id !== (int)$journal['currency_id']; + $useForeign = $this->default->id === (int)$journal['foreign_currency_id']; if ($usePrimary) { // Log::debug(sprintf('Journal #%d switches to primary currency amount (original is %s)', $journal['transaction_journal_id'], $journal['currency_code'])); $field = 'pc_amount'; @@ -88,7 +81,7 @@ class TransactionSummarizer if ($useForeign) { // Log::debug(sprintf('Journal #%d switches to foreign amount (foreign is %s)', $journal['transaction_journal_id'], $journal['foreign_currency_code'])); $field = 'foreign_amount'; - $currencyId = (int) $journal['foreign_currency_id']; + $currencyId = (int)$journal['foreign_currency_id']; $currencyName = $journal['foreign_currency_name']; $currencySymbol = $journal['foreign_currency_symbol']; $currencyCode = $journal['foreign_currency_code']; @@ -98,7 +91,7 @@ class TransactionSummarizer if (!$this->convertToPrimary) { // Log::debug('convertToPrimary is false.'); // use foreign amount? - $foreignCurrencyId = (int) $journal['foreign_currency_id']; + $foreignCurrencyId = (int)$journal['foreign_currency_id']; if (0 !== $foreignCurrencyId) { Log::debug(sprintf('Journal #%d also includes foreign amount (foreign is "%s")', $journal['transaction_journal_id'], $journal['foreign_currency_code'])); $foreignCurrencyName = $journal['foreign_currency_name']; @@ -109,7 +102,7 @@ class TransactionSummarizer } // first process normal amount - $amount = (string) ($journal[$field] ?? '0'); + $amount = (string)($journal[$field] ?? '0'); $array[$currencyId] ??= [ 'sum' => '0', 'currency_id' => $currencyId, @@ -128,7 +121,7 @@ class TransactionSummarizer // then process foreign amount, if it exists. if (0 !== $foreignCurrencyId && true === $includeForeign) { - $amount = (string) ($journal['foreign_amount'] ?? '0'); + $amount = (string)($journal['foreign_amount'] ?? '0'); $array[$foreignCurrencyId] ??= [ 'sum' => '0', 'currency_id' => $foreignCurrencyId, @@ -186,7 +179,7 @@ class TransactionSummarizer if ($convertToPrimary && $journal['currency_id'] !== $primary->id && $primary->id === $journal['foreign_currency_id']) { $field = 'foreign_amount'; } - $key = sprintf('%s-%s', $journal[$idKey], $currencyId); + $key = sprintf('%s-%s', $journal[$idKey], $currencyId); // sum it all up or create a new array. $array[$key] ??= [ 'id' => $journal[$idKey], @@ -200,15 +193,15 @@ class TransactionSummarizer ]; // add the data from the $field to the array. - $array[$key]['sum'] = bcadd($array[$key]['sum'], Steam::{$method}((string) ($journal[$field] ?? '0'))); // @phpstan-ignore-line + $array[$key]['sum'] = bcadd($array[$key]['sum'], Steam::{$method}((string)($journal[$field] ?? '0'))); // @phpstan-ignore-line Log::debug(sprintf('Field for transaction #%d is "%s" (%s). Sum: %s', $journal['transaction_group_id'], $currencyCode, $field, $array[$key]['sum'])); // also do foreign amount, but only when convertToPrimary is false (otherwise we have it already) // or when convertToPrimary is true and the foreign currency is ALSO not the default currency. - if ((!$convertToPrimary || $journal['foreign_currency_id'] !== $primary->id) && 0 !== (int) $journal['foreign_currency_id']) { + if ((!$convertToPrimary || $journal['foreign_currency_id'] !== $primary->id) && 0 !== (int)$journal['foreign_currency_id']) { Log::debug(sprintf('Use foreign amount from transaction #%d: %s %s. Sum: %s', $journal['transaction_group_id'], $currencyCode, $journal['foreign_amount'], $array[$key]['sum'])); $key = sprintf('%s-%s', $journal[$idKey], $journal['foreign_currency_id']); - $array[$key] ??= [ + $array[$key] ??= [ 'id' => $journal[$idKey], 'name' => $journal[$nameKey], 'sum' => '0', @@ -218,7 +211,7 @@ class TransactionSummarizer 'currency_code' => $journal['foreign_currency_code'], 'currency_decimal_places' => $journal['foreign_currency_decimal_places'], ]; - $array[$key]['sum'] = bcadd($array[$key]['sum'], Steam::{$method}((string) $journal['foreign_amount'])); // @phpstan-ignore-line + $array[$key]['sum'] = bcadd($array[$key]['sum'], Steam::{$method}((string)$journal['foreign_amount'])); // @phpstan-ignore-line } } @@ -230,4 +223,11 @@ class TransactionSummarizer Log::debug(sprintf('Overrule convertToPrimary to become %s', var_export($convertToPrimary, true))); $this->convertToPrimary = $convertToPrimary; } + + public function setUser(User $user): void + { + $this->user = $user; + $this->default = Amount::getPrimaryCurrencyByUserGroup($user->userGroup); + $this->convertToPrimary = Amount::convertToPrimary($user); + } } diff --git a/app/Support/Repositories/Recurring/CalculateRangeOccurrences.php b/app/Support/Repositories/Recurring/CalculateRangeOccurrences.php index 439fd1f50a..4ca2c8c29e 100644 --- a/app/Support/Repositories/Recurring/CalculateRangeOccurrences.php +++ b/app/Support/Repositories/Recurring/CalculateRangeOccurrences.php @@ -58,7 +58,7 @@ trait CalculateRangeOccurrences { $return = []; $attempts = 0; - $dayOfMonth = (int) $moment; + $dayOfMonth = (int)$moment; if ($start->day > $dayOfMonth) { // day has passed already, add a month. $start->addMonth(); @@ -82,8 +82,8 @@ trait CalculateRangeOccurrences */ protected function getNdomInRange(Carbon $start, Carbon $end, int $skipMod, string $moment): array { - $return = []; - $attempts = 0; + $return = []; + $attempts = 0; $start->startOfMonth(); // this feels a bit like a cop out but why reinvent the wheel? $counters = [1 => 'first', 2 => 'second', 3 => 'third', 4 => 'fourth', 5 => 'fifth']; @@ -108,12 +108,12 @@ trait CalculateRangeOccurrences */ protected function getWeeklyInRange(Carbon $start, Carbon $end, int $skipMod, string $moment): array { - $return = []; - $attempts = 0; + $return = []; + $attempts = 0; app('log')->debug('Rep is weekly.'); // monday = 1 // sunday = 7 - $dayOfWeek = (int) $moment; + $dayOfWeek = (int)$moment; app('log')->debug(sprintf('DoW in repetition is %d, in mutator is %d', $dayOfWeek, $start->dayOfWeekIso)); if ($start->dayOfWeekIso > $dayOfWeek) { // day has already passed this week, add one week: @@ -154,8 +154,8 @@ trait CalculateRangeOccurrences } // is $date between $start and $end? - $obj = clone $date; - $count = 0; + $obj = clone $date; + $count = 0; while ($obj <= $end && $obj >= $start && $count < 10) { if (0 === $attempts % $skipMod) { $return[] = clone $obj; diff --git a/app/Support/Repositories/Recurring/CalculateXOccurrences.php b/app/Support/Repositories/Recurring/CalculateXOccurrences.php index 04f07046d4..602cb03d02 100644 --- a/app/Support/Repositories/Recurring/CalculateXOccurrences.php +++ b/app/Support/Repositories/Recurring/CalculateXOccurrences.php @@ -63,7 +63,7 @@ trait CalculateXOccurrences $mutator = clone $date; $total = 0; $attempts = 0; - $dayOfMonth = (int) $moment; + $dayOfMonth = (int)$moment; if ($mutator->day > $dayOfMonth) { // day has passed already, add a month. $mutator->addMonth(); @@ -89,10 +89,10 @@ trait CalculateXOccurrences */ protected function getXNDomOccurrences(Carbon $date, int $count, int $skipMod, string $moment): array { - $return = []; - $total = 0; - $attempts = 0; - $mutator = clone $date; + $return = []; + $total = 0; + $attempts = 0; + $mutator = clone $date; $mutator->addDay(); // always assume today has passed. $mutator->startOfMonth(); // this feels a bit like a cop out but why reinvent the wheel? @@ -120,14 +120,14 @@ trait CalculateXOccurrences */ protected function getXWeeklyOccurrences(Carbon $date, int $count, int $skipMod, string $moment): array { - $return = []; - $total = 0; - $attempts = 0; - $mutator = clone $date; + $return = []; + $total = 0; + $attempts = 0; + $mutator = clone $date; // monday = 1 // sunday = 7 $mutator->addDay(); // always assume today has passed. - $dayOfWeek = (int) $moment; + $dayOfWeek = (int)$moment; if ($mutator->dayOfWeekIso > $dayOfWeek) { // day has already passed this week, add one week: $mutator->addWeek(); @@ -164,7 +164,7 @@ trait CalculateXOccurrences if ($mutator > $date) { $date->addYear(); } - $obj = clone $date; + $obj = clone $date; while ($total < $count) { if (0 === $attempts % $skipMod) { $return[] = clone $obj; diff --git a/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php b/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php index 215c11bf0a..bd4b44fd7d 100644 --- a/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php +++ b/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php @@ -68,7 +68,7 @@ trait CalculateXOccurrencesSince $mutator = clone $date; $total = 0; $attempts = 0; - $dayOfMonth = (int) $moment; + $dayOfMonth = (int)$moment; $dayOfMonth = 0 === $dayOfMonth ? 1 : $dayOfMonth; if ($mutator->day > $dayOfMonth) { Log::debug(sprintf('%d is after %d, add a month. Mutator is now...', $mutator->day, $dayOfMonth)); @@ -87,7 +87,7 @@ trait CalculateXOccurrencesSince ++$total; } ++$attempts; - $mutator = $mutator->endOfMonth()->addDay(); + $mutator = $mutator->endOfMonth()->addDay(); } Log::debug('Collected enough occurrences.'); @@ -103,10 +103,10 @@ trait CalculateXOccurrencesSince protected function getXNDomOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array { Log::debug(sprintf('Now in %s', __METHOD__)); - $return = []; - $total = 0; - $attempts = 0; - $mutator = clone $date; + $return = []; + $total = 0; + $attempts = 0; + $mutator = clone $date; $mutator->addDay(); // always assume today has passed. $mutator->startOfMonth(); // this feels a bit like a cop out but why reinvent the wheel? @@ -137,15 +137,15 @@ trait CalculateXOccurrencesSince protected function getXWeeklyOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array { Log::debug(sprintf('Now in %s', __METHOD__)); - $return = []; - $total = 0; - $attempts = 0; - $mutator = clone $date; + $return = []; + $total = 0; + $attempts = 0; + $mutator = clone $date; // monday = 1 // sunday = 7 // Removed assumption today has passed, see issue https://github.com/firefly-iii/firefly-iii/issues/4798 // $mutator->addDay(); // always assume today has passed. - $dayOfWeek = (int) $moment; + $dayOfWeek = (int)$moment; if ($mutator->dayOfWeekIso > $dayOfWeek) { // day has already passed this week, add one week: $mutator->addWeek(); @@ -189,7 +189,7 @@ trait CalculateXOccurrencesSince $date->addYear(); Log::debug(sprintf('Date is now %s', $date->format('Y-m-d'))); } - $obj = clone $date; + $obj = clone $date; while ($total < $count) { Log::debug(sprintf('total (%d) < count (%d) so go.', $total, $count)); Log::debug(sprintf('attempts (%d) %% skipmod (%d) === %d', $attempts, $skipMod, $attempts % $skipMod)); diff --git a/app/Support/Repositories/Recurring/FiltersWeekends.php b/app/Support/Repositories/Recurring/FiltersWeekends.php index 508e13f638..886679ced1 100644 --- a/app/Support/Repositories/Recurring/FiltersWeekends.php +++ b/app/Support/Repositories/Recurring/FiltersWeekends.php @@ -46,7 +46,7 @@ trait FiltersWeekends return $dates; } - $return = []; + $return = []; /** @var Carbon $date */ foreach ($dates as $date) { @@ -60,7 +60,7 @@ trait FiltersWeekends // is weekend and must set back to Friday? if (RecurrenceRepetitionWeekend::WEEKEND_TO_FRIDAY->value === $repetition->weekend) { - $clone = clone $date; + $clone = clone $date; $clone->addDays(5 - $date->dayOfWeekIso); Log::debug( sprintf('Date is %s, and this is in the weekend, so corrected to %s (Friday).', $date->format('D d M Y'), $clone->format('D d M Y')) @@ -72,7 +72,7 @@ trait FiltersWeekends // postpone to Monday? if (RecurrenceRepetitionWeekend::WEEKEND_TO_MONDAY->value === $repetition->weekend) { - $clone = clone $date; + $clone = clone $date; $clone->addDays(8 - $date->dayOfWeekIso); Log::debug( sprintf('Date is %s, and this is in the weekend, so corrected to %s (Monday).', $date->format('D d M Y'), $clone->format('D d M Y')) diff --git a/app/Support/Repositories/UserGroup/UserGroupInterface.php b/app/Support/Repositories/UserGroup/UserGroupInterface.php index d7a737b919..67e7fe3ea3 100644 --- a/app/Support/Repositories/UserGroup/UserGroupInterface.php +++ b/app/Support/Repositories/UserGroup/UserGroupInterface.php @@ -37,7 +37,7 @@ interface UserGroupInterface public function getUserGroup(): ?UserGroup; - public function setUser(Authenticatable|User|null $user): void; + public function setUser(Authenticatable | User | null $user): void; public function setUserGroup(UserGroup $userGroup): void; diff --git a/app/Support/Repositories/UserGroup/UserGroupTrait.php b/app/Support/Repositories/UserGroup/UserGroupTrait.php index 98781e5596..b6a1c94f5a 100644 --- a/app/Support/Repositories/UserGroup/UserGroupTrait.php +++ b/app/Support/Repositories/UserGroup/UserGroupTrait.php @@ -61,10 +61,10 @@ trait UserGroupTrait /** * @throws FireflyException */ - public function setUser(Authenticatable|User|null $user): void + public function setUser(Authenticatable | User | null $user): void { if ($user instanceof User) { - $this->user = $user; + $this->user = $user; if (null === $user->userGroup) { throw new FireflyException(sprintf('User #%d ("%s") has no user group.', $user->id, $user->email)); } @@ -99,15 +99,14 @@ trait UserGroupTrait public function setUserGroupById(int $userGroupId): void { $memberships = GroupMembership::where('user_id', $this->user->id) - ->where('user_group_id', $userGroupId) - ->count() - ; + ->where('user_group_id', $userGroupId) + ->count(); if (0 === $memberships) { throw new FireflyException(sprintf('User #%d has no access to administration #%d', $this->user->id, $userGroupId)); } /** @var null|UserGroup $userGroup */ - $userGroup = UserGroup::find($userGroupId); + $userGroup = UserGroup::find($userGroupId); if (null === $userGroup) { throw new FireflyException(sprintf('Cannot find administration for user #%d', $this->user->id)); } diff --git a/app/Support/Request/AppendsLocationData.php b/app/Support/Request/AppendsLocationData.php index 149898f986..01a6de40d3 100644 --- a/app/Support/Request/AppendsLocationData.php +++ b/app/Support/Request/AppendsLocationData.php @@ -46,19 +46,17 @@ trait AppendsLocationData return $return; } - private function validLongitude(string $longitude): bool - { - $number = (float) $longitude; - - return $number >= -180 && $number <= 180; - } - - private function validLatitude(string $latitude): bool - { - $number = (float) $latitude; - - return $number >= -90 && $number <= 90; - } + /** + * Abstract method stolen from "InteractsWithInput". + * + * @param null $key + * @param bool $default + * + * @return mixed + * + * @SuppressWarnings("PHPMD.BooleanArgumentFlag") + */ + abstract public function boolean($key = null, $default = false); /** * Abstract method. @@ -69,6 +67,22 @@ trait AppendsLocationData */ abstract public function has($key); + /** + * Abstract method. + * + * @return string + */ + abstract public function method(); + + /** + * Abstract method. + * + * @param mixed ...$patterns + * + * @return mixed + */ + abstract public function routeIs(...$patterns); + /** * Read the submitted Request data and add new or updated Location data to the array. */ @@ -82,12 +96,12 @@ trait AppendsLocationData $data['latitude'] = null; $data['zoom_level'] = null; - $longitudeKey = $this->getLocationKey($prefix, 'longitude'); - $latitudeKey = $this->getLocationKey($prefix, 'latitude'); - $zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level'); - $isValidPOST = $this->isValidPost($prefix); - $isValidPUT = $this->isValidPUT($prefix); - $isValidEmptyPUT = $this->isValidEmptyPUT($prefix); + $longitudeKey = $this->getLocationKey($prefix, 'longitude'); + $latitudeKey = $this->getLocationKey($prefix, 'latitude'); + $zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level'); + $isValidPOST = $this->isValidPost($prefix); + $isValidPUT = $this->isValidPUT($prefix); + $isValidEmptyPUT = $this->isValidEmptyPUT($prefix); // for a POST (store), all fields must be present and not NULL. if ($isValidPOST) { @@ -132,72 +146,22 @@ trait AppendsLocationData return sprintf('%s_%s', $prefix, $key); } - private function isValidPost(?string $prefix): bool + private function isValidEmptyPUT(?string $prefix): bool { - app('log')->debug('Now in isValidPost()'); - $longitudeKey = $this->getLocationKey($prefix, 'longitude'); - $latitudeKey = $this->getLocationKey($prefix, 'latitude'); - $zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level'); - $hasLocationKey = $this->getLocationKey($prefix, 'has_location'); - // fields must not be null: - if (null !== $this->get($longitudeKey) && null !== $this->get($latitudeKey) && null !== $this->get($zoomLevelKey)) { - app('log')->debug('All fields present'); - // if is POST and route contains API, this is enough: - if ('POST' === $this->method() && $this->routeIs('api.v1.*')) { - app('log')->debug('Is API location'); + $longitudeKey = $this->getLocationKey($prefix, 'longitude'); + $latitudeKey = $this->getLocationKey($prefix, 'latitude'); + $zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level'); - return true; - } - // if is POST and route does not contain API, must also have "has_location" = true - if ('POST' === $this->method() && $this->routeIs('*.store') && !$this->routeIs('api.v1.*') && '' !== $hasLocationKey) { - app('log')->debug('Is POST + store route.'); - $hasLocation = $this->boolean($hasLocationKey); - if (true === $hasLocation) { - app('log')->debug('Has form form location'); - - return true; - } - app('log')->debug('Does not have form location'); - - return false; - } - app('log')->debug('Is not POST API or POST form'); - - return false; - } - app('log')->debug('Fields not present'); - - return false; + return ( + null === $this->get($longitudeKey) + && null === $this->get($latitudeKey) + && null === $this->get($zoomLevelKey)) + && ( + 'PUT' === $this->method() + || ('POST' === $this->method() && $this->routeIs('*.update')) + ); } - /** - * Abstract method. - * - * @return string - */ - abstract public function method(); - - /** - * Abstract method. - * - * @param mixed ...$patterns - * - * @return mixed - */ - abstract public function routeIs(...$patterns); - - /** - * Abstract method stolen from "InteractsWithInput". - * - * @param null $key - * @param bool $default - * - * @return mixed - * - * @SuppressWarnings("PHPMD.BooleanArgumentFlag") - */ - abstract public function boolean($key = null, $default = false); - private function isValidPUT(?string $prefix): bool { $longitudeKey = $this->getLocationKey($prefix, 'longitude'); @@ -238,19 +202,55 @@ trait AppendsLocationData return false; } - private function isValidEmptyPUT(?string $prefix): bool + private function isValidPost(?string $prefix): bool { - $longitudeKey = $this->getLocationKey($prefix, 'longitude'); - $latitudeKey = $this->getLocationKey($prefix, 'latitude'); - $zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level'); + app('log')->debug('Now in isValidPost()'); + $longitudeKey = $this->getLocationKey($prefix, 'longitude'); + $latitudeKey = $this->getLocationKey($prefix, 'latitude'); + $zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level'); + $hasLocationKey = $this->getLocationKey($prefix, 'has_location'); + // fields must not be null: + if (null !== $this->get($longitudeKey) && null !== $this->get($latitudeKey) && null !== $this->get($zoomLevelKey)) { + app('log')->debug('All fields present'); + // if is POST and route contains API, this is enough: + if ('POST' === $this->method() && $this->routeIs('api.v1.*')) { + app('log')->debug('Is API location'); - return ( - null === $this->get($longitudeKey) - && null === $this->get($latitudeKey) - && null === $this->get($zoomLevelKey)) - && ( - 'PUT' === $this->method() - || ('POST' === $this->method() && $this->routeIs('*.update')) - ); + return true; + } + // if is POST and route does not contain API, must also have "has_location" = true + if ('POST' === $this->method() && $this->routeIs('*.store') && !$this->routeIs('api.v1.*') && '' !== $hasLocationKey) { + app('log')->debug('Is POST + store route.'); + $hasLocation = $this->boolean($hasLocationKey); + if (true === $hasLocation) { + app('log')->debug('Has form form location'); + + return true; + } + app('log')->debug('Does not have form location'); + + return false; + } + app('log')->debug('Is not POST API or POST form'); + + return false; + } + app('log')->debug('Fields not present'); + + return false; + } + + private function validLatitude(string $latitude): bool + { + $number = (float)$latitude; + + return $number >= -90 && $number <= 90; + } + + private function validLongitude(string $longitude): bool + { + $number = (float)$longitude; + + return $number >= -180 && $number <= 180; } } diff --git a/app/Support/Request/ChecksLogin.php b/app/Support/Request/ChecksLogin.php index 9fd2e11883..8576b17473 100644 --- a/app/Support/Request/ChecksLogin.php +++ b/app/Support/Request/ChecksLogin.php @@ -40,7 +40,7 @@ trait ChecksLogin { app('log')->debug(sprintf('Now in %s', __METHOD__)); // Only allow logged-in users - $check = auth()->check(); + $check = auth()->check(); if (!$check) { return false; } @@ -79,19 +79,19 @@ trait ChecksLogin public function getUserGroup(): ?UserGroup { /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); app('log')->debug('Now in getUserGroup()'); /** @var null|UserGroup $userGroup */ $userGroup = $this->route()?->parameter('userGroup'); if (null === $userGroup) { app('log')->debug('Request class has no userGroup parameter, but perhaps there is a parameter.'); - $userGroupId = (int) $this->get('user_group_id'); + $userGroupId = (int)$this->get('user_group_id'); if (0 === $userGroupId) { app('log')->debug(sprintf('Request class has no user_group_id parameter, grab default from user (group #%d).', $user->user_group_id)); - $userGroupId = (int) $user->user_group_id; + $userGroupId = (int)$user->user_group_id; } - $userGroup = UserGroup::find($userGroupId); + $userGroup = UserGroup::find($userGroupId); if (null === $userGroup) { app('log')->error(sprintf('Request class has user_group_id (#%d), but group does not exist.', $userGroupId)); diff --git a/app/Support/Request/ConvertsDataTypes.php b/app/Support/Request/ConvertsDataTypes.php index bc8da96efb..aa3896eb72 100644 --- a/app/Support/Request/ConvertsDataTypes.php +++ b/app/Support/Request/ConvertsDataTypes.php @@ -31,7 +31,6 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\Facades\Steam; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; - use function Safe\preg_replace; /** @@ -99,28 +98,6 @@ trait ConvertsDataTypes return Steam::filterSpaces($string); } - public function convertSortParameters(string $field, string $class): array - { - // assume this all works, because the validator would have caught any errors. - $parameter = (string)request()->query->get($field); - if ('' === $parameter) { - return []; - } - $parts = explode(',', $parameter); - $sortParameters = []; - foreach ($parts as $part) { - $part = trim($part); - $direction = 'asc'; - if ('-' === $part[0]) { - $part = substr($part, 1); - $direction = 'desc'; - } - $sortParameters[] = [$part, $direction]; - } - - return $sortParameters; - } - public function clearString(?string $string): ?string { $string = $this->clearStringKeepNewlines($string); @@ -159,6 +136,36 @@ trait ConvertsDataTypes return Steam::filterSpaces($this->convertString($field)); } + /** + * Return integer value. + */ + public function convertInteger(string $field): int + { + return (int)$this->get($field); + } + + public function convertSortParameters(string $field, string $class): array + { + // assume this all works, because the validator would have caught any errors. + $parameter = (string)request()->query->get($field); + if ('' === $parameter) { + return []; + } + $parts = explode(',', $parameter); + $sortParameters = []; + foreach ($parts as $part) { + $part = trim($part); + $direction = 'asc'; + if ('-' === $part[0]) { + $part = substr($part, 1); + $direction = 'desc'; + } + $sortParameters[] = [$part, $direction]; + } + + return $sortParameters; + } + /** * Return string value. */ @@ -178,14 +185,6 @@ trait ConvertsDataTypes */ abstract public function get(string $key, mixed $default = null): mixed; - /** - * Return integer value. - */ - public function convertInteger(string $field): int - { - return (int)$this->get($field); - } - /** * TODO duplicate, see SelectTransactionsRequest * @@ -218,6 +217,16 @@ trait ConvertsDataTypes return $collection; } + /** + * Abstract method that always exists in the Request classes that use this + * trait, OR a stub needs to be added by any other class that uses this train. + * + * @param mixed $key + * + * @return mixed + */ + abstract public function has($key); + /** * Return string value with newlines. */ @@ -386,16 +395,6 @@ trait ConvertsDataTypes return $return; } - /** - * Abstract method that always exists in the Request classes that use this - * trait, OR a stub needs to be added by any other class that uses this train. - * - * @param mixed $key - * - * @return mixed - */ - abstract public function has($key); - /** * Return date or NULL. */ @@ -418,6 +417,21 @@ trait ConvertsDataTypes return $result; } + /** + * Parse to integer + */ + protected function integerFromValue(?string $string): ?int + { + if (null === $string) { + return null; + } + if ('' === $string) { + return null; + } + + return (int)$string; + } + /** * Return integer value, or NULL when it's not set. */ @@ -445,7 +459,7 @@ trait ConvertsDataTypes if (!is_array($entry)) { continue; } - $amount = null; + $amount = null; if (array_key_exists('current_amount', $entry)) { $amount = $this->clearString((string)($entry['current_amount'] ?? '0')); if (null === $entry['current_amount']) { @@ -463,19 +477,4 @@ trait ConvertsDataTypes return $return; } - - /** - * Parse to integer - */ - protected function integerFromValue(?string $string): ?int - { - if (null === $string) { - return null; - } - if ('' === $string) { - return null; - } - - return (int)$string; - } } diff --git a/app/Support/Request/GetRecurrenceData.php b/app/Support/Request/GetRecurrenceData.php index 50dc0cd8f2..40f23738dd 100644 --- a/app/Support/Request/GetRecurrenceData.php +++ b/app/Support/Request/GetRecurrenceData.php @@ -38,12 +38,12 @@ trait GetRecurrenceData foreach ($stringKeys as $key) { if (array_key_exists($key, $transaction)) { - $return[$key] = (string) $transaction[$key]; + $return[$key] = (string)$transaction[$key]; } } foreach ($intKeys as $key) { if (array_key_exists($key, $transaction)) { - $return[$key] = (int) $transaction[$key]; + $return[$key] = (int)$transaction[$key]; } } foreach ($keys as $key) { diff --git a/app/Support/Request/ValidatesWebhooks.php b/app/Support/Request/ValidatesWebhooks.php index 5647184ef4..dff1541fde 100644 --- a/app/Support/Request/ValidatesWebhooks.php +++ b/app/Support/Request/ValidatesWebhooks.php @@ -25,10 +25,10 @@ declare(strict_types=1); namespace FireflyIII\Support\Request; -use Illuminate\Validation\Validator; use FireflyIII\Enums\WebhookTrigger; use FireflyIII\Models\Webhook; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; trait ValidatesWebhooks { @@ -40,9 +40,9 @@ trait ValidatesWebhooks if (count($validator->failed()) > 0) { return; } - $data = $validator->getData(); - $triggers = $data['triggers'] ?? []; - $responses = $data['responses'] ?? []; + $data = $validator->getData(); + $triggers = $data['triggers'] ?? []; + $responses = $data['responses'] ?? []; if (0 === count($triggers) || 0 === count($responses)) { Log::debug('No trigger or response, return.'); diff --git a/app/Support/Search/AccountSearch.php b/app/Support/Search/AccountSearch.php index 99b89f9c99..fe6e817c72 100644 --- a/app/Support/Search/AccountSearch.php +++ b/app/Support/Search/AccountSearch.php @@ -28,7 +28,6 @@ use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Collection; - use function Safe\json_encode; /** @@ -37,16 +36,16 @@ use function Safe\json_encode; class AccountSearch implements GenericSearchInterface { /** @var string */ - public const string SEARCH_ALL = 'all'; + public const string SEARCH_ALL = 'all'; /** @var string */ - public const string SEARCH_IBAN = 'iban'; + public const string SEARCH_IBAN = 'iban'; /** @var string */ - public const string SEARCH_ID = 'id'; + public const string SEARCH_ID = 'id'; /** @var string */ - public const string SEARCH_NAME = 'name'; + public const string SEARCH_NAME = 'name'; /** @var string */ public const string SEARCH_NUMBER = 'number'; @@ -63,10 +62,9 @@ class AccountSearch implements GenericSearchInterface public function search(): Collection { $searchQuery = $this->user->accounts() - ->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id') - ->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id') - ->whereIn('account_types.type', $this->types) - ; + ->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id') + ->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id') + ->whereIn('account_types.type', $this->types); $like = sprintf('%%%s%%', $this->query); $originalQuery = $this->query; @@ -92,7 +90,7 @@ class AccountSearch implements GenericSearchInterface break; case self::SEARCH_ID: - $searchQuery->where('accounts.id', '=', (int) $originalQuery); + $searchQuery->where('accounts.id', '=', (int)$originalQuery); break; @@ -137,7 +135,7 @@ class AccountSearch implements GenericSearchInterface $this->types = $types; } - public function setUser(Authenticatable|User|null $user): void + public function setUser(Authenticatable | User | null $user): void { if ($user instanceof User) { $this->user = $user; diff --git a/app/Support/Search/OperatorQuerySearch.php b/app/Support/Search/OperatorQuerySearch.php index dc975609f0..2e813bd125 100644 --- a/app/Support/Search/OperatorQuerySearch.php +++ b/app/Support/Search/OperatorQuerySearch.php @@ -105,6 +105,41 @@ class OperatorQuerySearch implements SearchInterface $this->currencyRepository = app(CurrencyRepositoryInterface::class); } + /** + * @throws FireflyException + */ + public static function getRootOperator(string $operator): string + { + $original = $operator; + // if the string starts with "-" (not), we can remove it and recycle + // the configuration from the original operator. + if (str_starts_with($operator, '-')) { + $operator = substr($operator, 1); + } + + $config = config(sprintf('search.operators.%s', $operator)); + if (null === $config) { + throw new FireflyException(sprintf('No configuration for search operator "%s"', $operator)); + } + if (true === $config['alias']) { + $return = $config['alias_for']; + if (str_starts_with($original, '-')) { + $return = sprintf('-%s', $config['alias_for']); + } + Log::debug(sprintf('"%s" is an alias for "%s", so return that instead.', $original, $return)); + + return $return; + } + Log::debug(sprintf('"%s" is not an alias.', $operator)); + + return $original; + } + + public function getExcludedWords(): array + { + return $this->prohibitedWords; + } + public function getInvalidOperators(): array { return $this->invalidOperators; @@ -120,6 +155,11 @@ class OperatorQuerySearch implements SearchInterface return $this->operators; } + public function getWords(): array + { + return $this->words; + } + public function getWordsAsString(): string { return implode(' ', $this->words); @@ -146,7 +186,7 @@ class OperatorQuerySearch implements SearchInterface try { $parsedQuery = $parser->parse($query); - } catch (LogicException|TypeError $e) { + } catch (LogicException | TypeError $e) { Log::error($e->getMessage()); Log::error(sprintf('Could not parse search: "%s".', $query)); @@ -163,6 +203,124 @@ class OperatorQuerySearch implements SearchInterface $this->collector->excludeSearchWords($this->prohibitedWords); } + public function searchTime(): float + { + return microtime(true) - $this->startTime; + } + + public function searchTransactions(): LengthAwarePaginator + { + $this->parseTagInstructions(); + if (0 === count($this->getWords()) && 0 === count($this->getExcludedWords()) && 0 === count($this->getOperators())) { + return new LengthAwarePaginator([], 0, 5, 1); + } + + return $this->collector->getPaginatedGroups(); + } + + public function setDate(Carbon $date): void + { + $this->date = $date; + } + + public function setLimit(int $limit): void + { + $this->limit = $limit; + $this->collector->setLimit($this->limit); + } + + public function setPage(int $page): void + { + $this->page = $page; + $this->collector->setPage($this->page); + } + + public function setUser(User $user): void + { + $this->accountRepository->setUser($user); + $this->billRepository->setUser($user); + $this->categoryRepository->setUser($user); + $this->budgetRepository->setUser($user); + $this->tagRepository->setUser($user); + $this->collector = app(GroupCollectorInterface::class); + $this->collector->setUser($user); + $this->collector->withAccountInformation()->withCategoryInformation()->withBudgetInformation(); + + $this->setLimit((int)app('preferences')->getForUser($user, 'listPageSize', 50)->data); + } + + private function findCurrency(string $value): ?TransactionCurrency + { + if (str_contains($value, '(') && str_contains($value, ')')) { + // bad method to split and get the currency code: + $parts = explode(' ', $value); + $value = trim($parts[count($parts) - 1], "() \t\n\r\0\x0B"); + } + $result = $this->currencyRepository->findByCode($value); + if (null === $result) { + return $this->currencyRepository->findByName($value); + } + + return $result; + } + + private function getCashAccount(): Account + { + return $this->accountRepository->getCashAccount(); + } + + /** + * @throws FireflyException + */ + private function handleFieldNode(FieldNode $node, bool $flipProhibitedFlag): void + { + $operator = strtolower($node->getOperator()); + $value = $node->getValue(); + $prohibited = $node->isProhibited($flipProhibitedFlag); + + $context = config(sprintf('search.operators.%s.needs_context', $operator)); + + // is an operator that needs no context, and value is false, then prohibited = true. + if ('false' === $value && in_array($operator, $this->validOperators, true) && false === $context && !$prohibited) { + $prohibited = true; + $value = 'true'; + } + // if the operator is prohibited, but the value is false, do an uno reverse + if ('false' === $value && $prohibited && in_array($operator, $this->validOperators, true) && false === $context) { + $prohibited = false; + $value = 'true'; + } + + // must be valid operator: + $inArray = in_array($operator, $this->validOperators, true); + if ($inArray) { + if ($this->updateCollector($operator, $value, $prohibited)) { + $this->operators->push([ + 'type' => self::getRootOperator($operator), + 'value' => $value, + 'prohibited' => $prohibited, + ]); + Log::debug(sprintf('Added operator type "%s"', $operator)); + } + } + if (!$inArray) { + Log::debug(sprintf('Added INVALID operator type "%s"', $operator)); + $this->invalidOperators[] = [ + 'type' => $operator, + 'value' => $value, + ]; + } + } + + private function handleNodeGroup(NodeGroup $node, bool $flipProhibitedFlag): void + { + $prohibited = $node->isProhibited($flipProhibitedFlag); + + foreach ($node->getNodes() as $subNode) { + $this->handleSearchNode($subNode, $prohibited); + } + } + /** * @throws FireflyException * @@ -197,7 +355,7 @@ class OperatorQuerySearch implements SearchInterface private function handleStringNode(StringNode $node, bool $flipProhibitedFlag): void { - $string = $node->getValue(); + $string = $node->getValue(); $prohibited = $node->isProhibited($flipProhibitedFlag); @@ -214,43 +372,857 @@ class OperatorQuerySearch implements SearchInterface /** * @throws FireflyException */ - private function handleFieldNode(FieldNode $node, bool $flipProhibitedFlag): void + private function parseDateRange(string $type, string $value): array { - $operator = strtolower($node->getOperator()); - $value = $node->getValue(); - $prohibited = $node->isProhibited($flipProhibitedFlag); - - $context = config(sprintf('search.operators.%s.needs_context', $operator)); - - // is an operator that needs no context, and value is false, then prohibited = true. - if ('false' === $value && in_array($operator, $this->validOperators, true) && false === $context && !$prohibited) { - $prohibited = true; - $value = 'true'; - } - // if the operator is prohibited, but the value is false, do an uno reverse - if ('false' === $value && $prohibited && in_array($operator, $this->validOperators, true) && false === $context) { - $prohibited = false; - $value = 'true'; + $parser = new ParseDateString(); + if ($parser->isDateRange($value)) { + return $parser->parseRange($value); } - // must be valid operator: - $inArray = in_array($operator, $this->validOperators, true); - if ($inArray) { - if ($this->updateCollector($operator, $value, $prohibited)) { - $this->operators->push([ - 'type' => self::getRootOperator($operator), - 'value' => $value, - 'prohibited' => $prohibited, - ]); - Log::debug(sprintf('Added operator type "%s"', $operator)); - } - } - if (!$inArray) { - Log::debug(sprintf('Added INVALID operator type "%s"', $operator)); + try { + $parsedDate = $parser->parseDate($value); + } catch (FireflyException) { + Log::debug(sprintf('Could not parse date "%s", will return empty array.', $value)); $this->invalidOperators[] = [ - 'type' => $operator, + 'type' => $type, 'value' => $value, ]; + + return []; + } + + return [ + 'exact' => $parsedDate, + ]; + } + + private function parseTagInstructions(): void + { + Log::debug('Now in parseTagInstructions()'); + // if exclude tags, remove excluded tags. + if (count($this->excludeTags) > 0) { + Log::debug(sprintf('%d exclude tag(s)', count($this->excludeTags))); + $collection = new Collection(); + foreach ($this->excludeTags as $tagId) { + $tag = $this->tagRepository->find($tagId); + if (null !== $tag) { + Log::debug(sprintf('Exclude tag "%s"', $tag->tag)); + $collection->push($tag); + } + } + Log::debug(sprintf('Selecting all tags except %d excluded tag(s).', $collection->count())); + $this->collector->setWithoutSpecificTags($collection); + } + // if include tags, include them: + if (count($this->includeTags) > 0) { + Log::debug(sprintf('%d include tag(s)', count($this->includeTags))); + $collection = new Collection(); + foreach ($this->includeTags as $tagId) { + $tag = $this->tagRepository->find($tagId); + if (null !== $tag) { + Log::debug(sprintf('Include tag "%s"', $tag->tag)); + $collection->push($tag); + } + } + $this->collector->setAllTags($collection); + } + // if include ANY tags, include them: (see #8632) + if (count($this->includeAnyTags) > 0) { + Log::debug(sprintf('%d include ANY tag(s)', count($this->includeAnyTags))); + $collection = new Collection(); + foreach ($this->includeAnyTags as $tagId) { + $tag = $this->tagRepository->find($tagId); + if (null !== $tag) { + Log::debug(sprintf('Include ANY tag "%s"', $tag->tag)); + $collection->push($tag); + } + } + $this->collector->setTags($collection); + } + } + + /** + * searchDirection: 1 = source (default), 2 = destination, 3 = both + * stringPosition: 1 = start (default), 2 = end, 3 = contains, 4 = is + * + * @SuppressWarnings("PHPMD.BooleanArgumentFlag") + * @SuppressWarnings("PHPMD.NPathComplexity") + */ + private function searchAccount(string $value, SearchDirection $searchDirection, StringPosition $stringPosition, bool $prohibited = false): void + { + Log::debug(sprintf('searchAccount("%s", %s, %s)', $value, $stringPosition->name, $searchDirection->name)); + + // search direction (default): for source accounts + $searchTypes = [AccountTypeEnum::ASSET->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::REVENUE->value]; + $collectorMethod = 'setSourceAccounts'; + if ($prohibited) { + $collectorMethod = 'excludeSourceAccounts'; + } + + // search direction: for destination accounts + if (SearchDirection::DESTINATION === $searchDirection) { // destination + // destination can be + $searchTypes = [AccountTypeEnum::ASSET->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::EXPENSE->value]; + $collectorMethod = 'setDestinationAccounts'; + if ($prohibited) { + $collectorMethod = 'excludeDestinationAccounts'; + } + } + // either account could be: + if (SearchDirection::BOTH === $searchDirection) { + $searchTypes = [AccountTypeEnum::ASSET->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::EXPENSE->value, AccountTypeEnum::REVENUE->value]; + $collectorMethod = 'setAccounts'; + if ($prohibited) { + $collectorMethod = 'excludeAccounts'; + } + } + // string position (default): starts with: + $stringMethod = 'str_starts_with'; + + // string position: ends with: + if (StringPosition::ENDS === $stringPosition) { + $stringMethod = 'str_ends_with'; + } + if (StringPosition::CONTAINS === $stringPosition) { + $stringMethod = 'str_contains'; + } + if (StringPosition::IS === $stringPosition) { + $stringMethod = 'stringIsEqual'; + } + + // get accounts: + $accounts = $this->accountRepository->searchAccount($value, $searchTypes, 1337); + if (0 === $accounts->count() && false === $prohibited) { + Log::warning('Found zero accounts, search for non existing account, NO results will be returned.'); + $this->collector->findNothing(); + + return; + } + if (0 === $accounts->count() && true === $prohibited) { + Log::debug('Found zero accounts, but the search is negated, so effectively we ignore the search parameter.'); + + return; + } + Log::debug(sprintf('Found %d accounts, will filter.', $accounts->count())); + $filtered = $accounts->filter( + static fn(Account $account) => $stringMethod(strtolower($account->name), strtolower($value)) + ); + + if (0 === $filtered->count()) { + Log::warning('Left with zero accounts, so cannot find anything, NO results will be returned.'); + $this->collector->findNothing(); + + return; + } + Log::debug(sprintf('Left with %d, set as %s().', $filtered->count(), $collectorMethod)); + $this->collector->{$collectorMethod}($filtered); // @phpstan-ignore-line + } + + /** + * TODO make enums + * searchDirection: 1 = source (default), 2 = destination, 3 = both + * stringPosition: 1 = start (default), 2 = end, 3 = contains, 4 = is + * + * @SuppressWarnings("PHPMD.BooleanArgumentFlag") + * @SuppressWarnings("PHPMD.NPathComplexity") + */ + private function searchAccountNr(string $value, SearchDirection $searchDirection, StringPosition $stringPosition, bool $prohibited = false): void + { + Log::debug(sprintf('searchAccountNr(%s, %d, %d)', $value, $searchDirection->name, $stringPosition->name)); + + // search direction (default): for source accounts + $searchTypes = [AccountTypeEnum::ASSET->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::REVENUE->value]; + $collectorMethod = 'setSourceAccounts'; + if (true === $prohibited) { + $collectorMethod = 'excludeSourceAccounts'; + } + + // search direction: for destination accounts + if (SearchDirection::DESTINATION === $searchDirection) { + // destination can be + $searchTypes = [AccountTypeEnum::ASSET->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::EXPENSE->value]; + $collectorMethod = 'setDestinationAccounts'; + if (true === $prohibited) { + $collectorMethod = 'excludeDestinationAccounts'; + } + } + + // either account could be: + if (SearchDirection::BOTH === $searchDirection) { + $searchTypes = [AccountTypeEnum::ASSET->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::EXPENSE->value, AccountTypeEnum::REVENUE->value]; + $collectorMethod = 'setAccounts'; + if (true === $prohibited) { + $collectorMethod = 'excludeAccounts'; + } + } + + // string position (default): starts with: + $stringMethod = 'str_starts_with'; + + // string position: ends with: + if (StringPosition::ENDS === $stringPosition) { + $stringMethod = 'str_ends_with'; + } + if (StringPosition::CONTAINS === $stringPosition) { + $stringMethod = 'str_contains'; + } + if (StringPosition::IS === $stringPosition) { + $stringMethod = 'stringIsEqual'; + } + + // search for accounts: + $accounts = $this->accountRepository->searchAccountNr($value, $searchTypes, 1337); + if (0 === $accounts->count()) { + Log::debug('Found zero accounts, search for invalid account.'); + Log::warning('Call to findNothing() from searchAccountNr().'); + $this->collector->findNothing(); + + return; + } + + // if found, do filter + Log::debug(sprintf('Found %d accounts, will filter.', $accounts->count())); + $filtered = $accounts->filter( + static function (Account $account) use ($value, $stringMethod) { + // either IBAN or account number + $ibanMatch = $stringMethod(strtolower((string)$account->iban), strtolower($value)); + $accountNrMatch = false; + + /** @var AccountMeta $meta */ + foreach ($account->accountMeta as $meta) { + if ('account_number' === $meta->name && $stringMethod(strtolower((string)$meta->data), strtolower($value))) { + $accountNrMatch = true; + } + } + + return $ibanMatch || $accountNrMatch; + } + ); + + if (0 === $filtered->count()) { + Log::debug('Left with zero, search for invalid account'); + Log::warning('Call to findNothing() from searchAccountNr().'); + $this->collector->findNothing(); + + return; + } + Log::debug(sprintf('Left with %d, set as %s().', $filtered->count(), $collectorMethod)); + $this->collector->{$collectorMethod}($filtered); // @phpstan-ignore-line + } + + /** + * @throws FireflyException + * + * @SuppressWarnings("PHPMD.BooleanArgumentFlag") + */ + private function setDateAfterParams(array $range, bool $prohibited = false): void + { + /** + * @var string $key + * @var Carbon|string $value + */ + foreach ($range as $key => $value) { + $key = $prohibited ? sprintf('%s_not', $key) : $key; + + switch ($key) { + default: + throw new FireflyException(sprintf('Cannot handle key "%s" in setDateAfterParams()', $key)); + + case 'exact': + if ($value instanceof Carbon) { + $this->collector->setAfter($value); + $this->operators->push(['type' => 'date_after', 'value' => $value->format('Y-m-d')]); + } + + break; + + case 'year': + if (is_string($value)) { + Log::debug(sprintf('Set date_is_after YEAR value "%s"', $value)); + $this->collector->yearAfter($value); + $this->operators->push(['type' => 'date_after_year', 'value' => $value]); + } + + break; + + case 'month': + if (is_string($value)) { + Log::debug(sprintf('Set date_is_after MONTH value "%s"', $value)); + $this->collector->monthAfter($value); + $this->operators->push(['type' => 'date_after_month', 'value' => $value]); + } + + break; + + case 'day': + if (is_string($value)) { + Log::debug(sprintf('Set date_is_after DAY value "%s"', $value)); + $this->collector->dayAfter($value); + $this->operators->push(['type' => 'date_after_day', 'value' => $value]); + } + + break; + } + } + } + + /** + * @throws FireflyException + * + * @SuppressWarnings("PHPMD.BooleanArgumentFlag") + */ + private function setDateBeforeParams(array $range, bool $prohibited = false): void + { + /** + * @var string $key + * @var Carbon|string $value + */ + foreach ($range as $key => $value) { + $key = $prohibited ? sprintf('%s_not', $key) : $key; + + switch ($key) { + default: + throw new FireflyException(sprintf('Cannot handle key "%s" in setDateBeforeParams()', $key)); + + case 'exact': + if ($value instanceof Carbon) { + $this->collector->setBefore($value); + $this->operators->push(['type' => 'date_before', 'value' => $value->format('Y-m-d')]); + } + + break; + + case 'year': + if (is_string($value)) { + Log::debug(sprintf('Set date_is_before YEAR value "%s"', $value)); + $this->collector->yearBefore($value); + $this->operators->push(['type' => 'date_before_year', 'value' => $value]); + } + + break; + + case 'month': + if (is_string($value)) { + Log::debug(sprintf('Set date_is_before MONTH value "%s"', $value)); + $this->collector->monthBefore($value); + $this->operators->push(['type' => 'date_before_month', 'value' => $value]); + } + + break; + + case 'day': + if (is_string($value)) { + Log::debug(sprintf('Set date_is_before DAY value "%s"', $value)); + $this->collector->dayBefore($value); + $this->operators->push(['type' => 'date_before_day', 'value' => $value]); + } + + break; + } + } + } + + /** + * @throws FireflyException + * + * @SuppressWarnings("PHPMD.BooleanArgumentFlag") + */ + private function setExactDateParams(array $range, bool $prohibited = false): void + { + /** + * @var string $key + * @var Carbon|string $value + */ + foreach ($range as $key => $value) { + $key = $prohibited ? sprintf('%s_not', $key) : $key; + + switch ($key) { + default: + throw new FireflyException(sprintf('Cannot handle key "%s" in setExactParameters()', $key)); + + case 'exact': + if ($value instanceof Carbon) { + Log::debug(sprintf('Set date_is_exact value "%s"', $value->format('Y-m-d'))); + $this->collector->setRange($value, $value); + $this->operators->push(['type' => 'date_on', 'value' => $value->format('Y-m-d')]); + } + + break; + + case 'exact_not': + if ($value instanceof Carbon) { + $this->collector->excludeRange($value, $value); + $this->operators->push(['type' => 'not_date_on', 'value' => $value->format('Y-m-d')]); + } + + break; + + case 'year': + if (is_string($value)) { + Log::debug(sprintf('Set date_is_exact YEAR value "%s"', $value)); + $this->collector->yearIs($value); + $this->operators->push(['type' => 'date_on_year', 'value' => $value]); + } + + break; + + case 'year_not': + if (is_string($value)) { + Log::debug(sprintf('Set date_is_exact_not YEAR value "%s"', $value)); + $this->collector->yearIsNot($value); + $this->operators->push(['type' => 'not_date_on_year', 'value' => $value]); + } + + break; + + case 'month': + if (is_string($value)) { + Log::debug(sprintf('Set date_is_exact MONTH value "%s"', $value)); + $this->collector->monthIs($value); + $this->operators->push(['type' => 'date_on_month', 'value' => $value]); + } + + break; + + case 'month_not': + if (is_string($value)) { + Log::debug(sprintf('Set date_is_exact not MONTH value "%s"', $value)); + $this->collector->monthIsNot($value); + $this->operators->push(['type' => 'not_date_on_month', 'value' => $value]); + } + + break; + + case 'day': + if (is_string($value)) { + Log::debug(sprintf('Set date_is_exact DAY value "%s"', $value)); + $this->collector->dayIs($value); + $this->operators->push(['type' => 'date_on_day', 'value' => $value]); + } + + break; + + case 'day_not': + if (is_string($value)) { + Log::debug(sprintf('Set not date_is_exact DAY value "%s"', $value)); + $this->collector->dayIsNot($value); + $this->operators->push(['type' => 'not_date_on_day', 'value' => $value]); + } + + break; + } + } + } + + /** + * @throws FireflyException + * + * @SuppressWarnings("PHPMD.BooleanArgumentFlag") + */ + private function setExactMetaDateParams(string $field, array $range, bool $prohibited = false): void + { + Log::debug('Now in setExactMetaDateParams()'); + + /** + * @var string $key + * @var Carbon|string $value + */ + foreach ($range as $key => $value) { + $key = $prohibited ? sprintf('%s_not', $key) : $key; + + switch ($key) { + default: + throw new FireflyException(sprintf('Cannot handle key "%s" in setExactMetaDateParams()', $key)); + + case 'exact': + if ($value instanceof Carbon) { + Log::debug(sprintf('Set %s_is_exact value "%s"', $field, $value->format('Y-m-d'))); + $this->collector->setMetaDateRange($value, $value, $field); + $this->operators->push(['type' => sprintf('%s_on', $field), 'value' => $value->format('Y-m-d')]); + } + + break; + + case 'exact_not': + if ($value instanceof Carbon) { + Log::debug(sprintf('Set NOT %s_is_exact value "%s"', $field, $value->format('Y-m-d'))); + $this->collector->excludeMetaDateRange($value, $value, $field); + $this->operators->push(['type' => sprintf('not_%s_on', $field), 'value' => $value->format('Y-m-d')]); + } + + break; + + case 'year': + if (is_string($value)) { + Log::debug(sprintf('Set %s_is_exact YEAR value "%s"', $field, $value)); + $this->collector->metaYearIs($value, $field); + $this->operators->push(['type' => sprintf('%s_on_year', $field), 'value' => $value]); + } + + break; + + case 'year_not': + if (is_string($value)) { + Log::debug(sprintf('Set NOT %s_is_exact YEAR value "%s"', $field, $value)); + $this->collector->metaYearIsNot($value, $field); + $this->operators->push(['type' => sprintf('not_%s_on_year', $field), 'value' => $value]); + } + + break; + + case 'month': + if (is_string($value)) { + Log::debug(sprintf('Set %s_is_exact MONTH value "%s"', $field, $value)); + $this->collector->metaMonthIs($value, $field); + $this->operators->push(['type' => sprintf('%s_on_month', $field), 'value' => $value]); + } + + break; + + case 'month_not': + if (is_string($value)) { + Log::debug(sprintf('Set NOT %s_is_exact MONTH value "%s"', $field, $value)); + $this->collector->metaMonthIsNot($value, $field); + $this->operators->push(['type' => sprintf('not_%s_on_month', $field), 'value' => $value]); + } + + break; + + case 'day': + if (is_string($value)) { + Log::debug(sprintf('Set %s_is_exact DAY value "%s"', $field, $value)); + $this->collector->metaDayIs($value, $field); + $this->operators->push(['type' => sprintf('%s_on_day', $field), 'value' => $value]); + } + + break; + + case 'day_not': + if (is_string($value)) { + Log::debug(sprintf('Set NOT %s_is_exact DAY value "%s"', $field, $value)); + $this->collector->metaDayIsNot($value, $field); + $this->operators->push(['type' => sprintf('not_%s_on_day', $field), 'value' => $value]); + } + + break; + } + } + } + + /** + * @throws FireflyException + * + * @SuppressWarnings("PHPMD.BooleanArgumentFlag") + */ + private function setExactObjectDateParams(string $field, array $range, bool $prohibited = false): void + { + /** + * @var string $key + * @var Carbon|string $value + */ + foreach ($range as $key => $value) { + $key = $prohibited ? sprintf('%s_not', $key) : $key; + + switch ($key) { + default: + throw new FireflyException(sprintf('Cannot handle key "%s" in setExactObjectDateParams()', $key)); + + case 'exact': + if ($value instanceof Carbon) { + Log::debug(sprintf('Set %s_is_exact value "%s"', $field, $value->format('Y-m-d'))); + $this->collector->setObjectRange($value, clone $value, $field); + $this->operators->push(['type' => sprintf('%s_on', $field), 'value' => $value->format('Y-m-d')]); + } + + break; + + case 'exact_not': + if ($value instanceof Carbon) { + Log::debug(sprintf('Set NOT %s_is_exact value "%s"', $field, $value->format('Y-m-d'))); + $this->collector->excludeObjectRange($value, clone $value, $field); + $this->operators->push(['type' => sprintf('not_%s_on', $field), 'value' => $value->format('Y-m-d')]); + } + + break; + + case 'year': + if (is_string($value)) { + Log::debug(sprintf('Set %s_is_exact YEAR value "%s"', $field, $value)); + $this->collector->objectYearIs($value, $field); + $this->operators->push(['type' => sprintf('%s_on_year', $field), 'value' => $value]); + } + + break; + + case 'year_not': + if (is_string($value)) { + Log::debug(sprintf('Set NOT %s_is_exact YEAR value "%s"', $field, $value)); + $this->collector->objectYearIsNot($value, $field); + $this->operators->push(['type' => sprintf('not_%s_on_year', $field), 'value' => $value]); + } + + break; + + case 'month': + if (is_string($value)) { + Log::debug(sprintf('Set %s_is_exact MONTH value "%s"', $field, $value)); + $this->collector->objectMonthIs($value, $field); + $this->operators->push(['type' => sprintf('%s_on_month', $field), 'value' => $value]); + } + + break; + + case 'month_not': + if (is_string($value)) { + Log::debug(sprintf('Set NOT %s_is_exact MONTH value "%s"', $field, $value)); + $this->collector->objectMonthIsNot($value, $field); + $this->operators->push(['type' => sprintf('not_%s_on_month', $field), 'value' => $value]); + } + + break; + + case 'day': + if (is_string($value)) { + Log::debug(sprintf('Set %s_is_exact DAY value "%s"', $field, $value)); + $this->collector->objectDayIs($value, $field); + $this->operators->push(['type' => sprintf('%s_on_day', $field), 'value' => $value]); + } + + break; + + case 'day_not': + if (is_string($value)) { + Log::debug(sprintf('Set NOT %s_is_exact DAY value "%s"', $field, $value)); + $this->collector->objectDayIsNot($value, $field); + $this->operators->push(['type' => sprintf('not_%s_on_day', $field), 'value' => $value]); + } + + break; + } + } + } + + /** + * @throws FireflyException + * + * @SuppressWarnings("PHPMD.BooleanArgumentFlag") + */ + private function setMetaDateAfterParams(string $field, array $range, bool $prohibited = false): void + { + /** + * @var string $key + * @var Carbon|string $value + */ + foreach ($range as $key => $value) { + $key = $prohibited ? sprintf('%s_not', $key) : $key; + + switch ($key) { + default: + throw new FireflyException(sprintf('Cannot handle key "%s" in setMetaDateAfterParams()', $key)); + + case 'exact': + if ($value instanceof Carbon) { + $this->collector->setMetaAfter($value, $field); + $this->operators->push(['type' => sprintf('%s_after', $field), 'value' => $value->format('Y-m-d')]); + } + + break; + + case 'year': + if (is_string($value)) { + Log::debug(sprintf('Set %s_is_after YEAR value "%s"', $field, $value)); + $this->collector->metaYearAfter($value, $field); + $this->operators->push(['type' => sprintf('%s_after_year', $field), 'value' => $value]); + } + + break; + + case 'month': + if (is_string($value)) { + Log::debug(sprintf('Set %s_is_after MONTH value "%s"', $field, $value)); + $this->collector->metaMonthAfter($value, $field); + $this->operators->push(['type' => sprintf('%s_after_month', $field), 'value' => $value]); + } + + break; + + case 'day': + if (is_string($value)) { + Log::debug(sprintf('Set %s_is_after DAY value "%s"', $field, $value)); + $this->collector->metaDayAfter($value, $field); + $this->operators->push(['type' => sprintf('%s_after_day', $field), 'value' => $value]); + } + + break; + } + } + } + + /** + * @throws FireflyException + * + * @SuppressWarnings("PHPMD.BooleanArgumentFlag") + */ + private function setMetaDateBeforeParams(string $field, array $range, bool $prohibited = false): void + { + /** + * @var string $key + * @var Carbon|string $value + */ + foreach ($range as $key => $value) { + $key = $prohibited ? sprintf('%s_not', $key) : $key; + + switch ($key) { + default: + throw new FireflyException(sprintf('Cannot handle key "%s" in setMetaDateBeforeParams()', $key)); + + case 'exact': + if ($value instanceof Carbon) { + $this->collector->setMetaBefore($value, $field); + $this->operators->push(['type' => sprintf('%s_before', $field), 'value' => $value->format('Y-m-d')]); + } + + break; + + case 'year': + if (is_string($value)) { + Log::debug(sprintf('Set %s_is_before YEAR value "%s"', $field, $value)); + $this->collector->metaYearBefore($value, $field); + $this->operators->push(['type' => sprintf('%s_before_year', $field), 'value' => $value]); + } + + break; + + case 'month': + if (is_string($value)) { + Log::debug(sprintf('Set %s_is_before MONTH value "%s"', $field, $value)); + $this->collector->metaMonthBefore($value, $field); + $this->operators->push(['type' => sprintf('%s_before_month', $field), 'value' => $value]); + } + + break; + + case 'day': + if (is_string($value)) { + Log::debug(sprintf('Set %s_is_before DAY value "%s"', $field, $value)); + $this->collector->metaDayBefore($value, $field); + $this->operators->push(['type' => sprintf('%s_before_day', $field), 'value' => $value]); + } + + break; + } + } + } + + /** + * @throws FireflyException + * + * @SuppressWarnings("PHPMD.BooleanArgumentFlag") + */ + private function setObjectDateAfterParams(string $field, array $range, bool $prohibited = false): void + { + /** + * @var string $key + * @var Carbon|string $value + */ + foreach ($range as $key => $value) { + $key = $prohibited ? sprintf('%s_not', $key) : $key; + + switch ($key) { + default: + throw new FireflyException(sprintf('Cannot handle key "%s" in setObjectDateAfterParams()', $key)); + + case 'exact': + if ($value instanceof Carbon) { + $this->collector->setObjectAfter($value, $field); + $this->operators->push(['type' => sprintf('%s_after', $field), 'value' => $value->format('Y-m-d')]); + } + + break; + + case 'year': + if (is_string($value)) { + Log::debug(sprintf('Set date_is_after YEAR value "%s"', $value)); + $this->collector->objectYearAfter($value, $field); + $this->operators->push(['type' => sprintf('%s_after_year', $field), 'value' => $value]); + } + + break; + + case 'month': + if (is_string($value)) { + Log::debug(sprintf('Set date_is_after MONTH value "%s"', $value)); + $this->collector->objectMonthAfter($value, $field); + $this->operators->push(['type' => sprintf('%s_after_month', $field), 'value' => $value]); + } + + break; + + case 'day': + if (is_string($value)) { + Log::debug(sprintf('Set date_is_after DAY value "%s"', $value)); + $this->collector->objectDayAfter($value, $field); + $this->operators->push(['type' => sprintf('%s_after_day', $field), 'value' => $value]); + } + + break; + } + } + } + + /** + * @throws FireflyException + * + * @SuppressWarnings("PHPMD.BooleanArgumentFlag") + */ + private function setObjectDateBeforeParams(string $field, array $range, bool $prohibited = false): void + { + /** + * @var string $key + * @var Carbon|string $value + */ + foreach ($range as $key => $value) { + $key = $prohibited ? sprintf('%s_not', $key) : $key; + + switch ($key) { + default: + throw new FireflyException(sprintf('Cannot handle key "%s" in setObjectDateBeforeParams()', $key)); + + case 'exact': + if ($value instanceof Carbon) { + $this->collector->setObjectBefore($value, $field); + $this->operators->push(['type' => sprintf('%s_before', $field), 'value' => $value->format('Y-m-d')]); + } + + break; + + case 'year': + if (is_string($value)) { + Log::debug(sprintf('Set date_is_before YEAR value "%s"', $value)); + $this->collector->objectYearBefore($value, $field); + $this->operators->push(['type' => sprintf('%s_before_year', $field), 'value' => $value]); + } + + break; + + case 'month': + if (is_string($value)) { + Log::debug(sprintf('Set date_is_before MONTH value "%s"', $value)); + $this->collector->objectMonthBefore($value, $field); + $this->operators->push(['type' => sprintf('%s_before_month', $field), 'value' => $value]); + } + + break; + + case 'day': + if (is_string($value)) { + Log::debug(sprintf('Set date_is_before DAY value "%s"', $value)); + $this->collector->objectDayBefore($value, $field); + $this->operators->push(['type' => sprintf('%s_before_day', $field), 'value' => $value]); + } + + break; + } } } @@ -278,15 +1250,15 @@ class OperatorQuerySearch implements SearchInterface throw new FireflyException(sprintf('Unsupported search operator: "%s"', $operator)); - // some search operators are ignored, basically: + // some search operators are ignored, basically: case 'user_action': Log::info(sprintf('Ignore search operator "%s"', $operator)); return false; - // - // all account related searches: - // + // + // all account related searches: + // case 'account_is': $this->searchAccount($value, SearchDirection::BOTH, StringPosition::IS); @@ -448,7 +1420,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'source_account_id': - $account = $this->accountRepository->find((int) $value); + $account = $this->accountRepository->find((int)$value); if (null !== $account) { $this->collector->setSourceAccounts(new Collection()->push($account)); } @@ -461,7 +1433,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-source_account_id': - $account = $this->accountRepository->find((int) $value); + $account = $this->accountRepository->find((int)$value); if (null !== $account) { $this->collector->excludeSourceAccounts(new Collection()->push($account)); } @@ -474,25 +1446,25 @@ class OperatorQuerySearch implements SearchInterface break; case 'journal_id': - $parts = explode(',', $value); + $parts = explode(',', $value); $this->collector->setJournalIds($parts); break; case '-journal_id': - $parts = explode(',', $value); + $parts = explode(',', $value); $this->collector->excludeJournalIds($parts); break; case 'id': - $parts = explode(',', $value); + $parts = explode(',', $value); $this->collector->setIds($parts); break; case '-id': - $parts = explode(',', $value); + $parts = explode(',', $value); $this->collector->excludeIds($parts); break; @@ -578,7 +1550,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'destination_account_id': - $account = $this->accountRepository->find((int) $value); + $account = $this->accountRepository->find((int)$value); if (null !== $account) { $this->collector->setDestinationAccounts(new Collection()->push($account)); } @@ -590,7 +1562,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-destination_account_id': - $account = $this->accountRepository->find((int) $value); + $account = $this->accountRepository->find((int)$value); if (null !== $account) { $this->collector->excludeDestinationAccounts(new Collection()->push($account)); } @@ -603,12 +1575,12 @@ class OperatorQuerySearch implements SearchInterface case 'account_id': Log::debug(sprintf('Now in "account_id" with value "%s"', $value)); - $parts = explode(',', $value); - $collection = new Collection(); + $parts = explode(',', $value); + $collection = new Collection(); foreach ($parts as $accountId) { - $accountId = (int) $accountId; + $accountId = (int)$accountId; Log::debug(sprintf('Searching for account with ID #%d', $accountId)); - $account = $this->accountRepository->find($accountId); + $account = $this->accountRepository->find($accountId); if (null !== $account) { Log::debug(sprintf('Found account with ID #%d ("%s")', $accountId, $account->name)); $collection->push($account); @@ -629,10 +1601,10 @@ class OperatorQuerySearch implements SearchInterface break; case '-account_id': - $parts = explode(',', $value); - $collection = new Collection(); + $parts = explode(',', $value); + $collection = new Collection(); foreach ($parts as $accountId) { - $account = $this->accountRepository->find((int) $accountId); + $account = $this->accountRepository->find((int)$accountId); if (null !== $account) { $collection->push($account); } @@ -647,48 +1619,48 @@ class OperatorQuerySearch implements SearchInterface break; - // - // cash account - // + // + // cash account + // case 'source_is_cash': - $account = $this->getCashAccount(); + $account = $this->getCashAccount(); $this->collector->setSourceAccounts(new Collection()->push($account)); break; case '-source_is_cash': - $account = $this->getCashAccount(); + $account = $this->getCashAccount(); $this->collector->excludeSourceAccounts(new Collection()->push($account)); break; case 'destination_is_cash': - $account = $this->getCashAccount(); + $account = $this->getCashAccount(); $this->collector->setDestinationAccounts(new Collection()->push($account)); break; case '-destination_is_cash': - $account = $this->getCashAccount(); + $account = $this->getCashAccount(); $this->collector->excludeDestinationAccounts(new Collection()->push($account)); break; case 'account_is_cash': - $account = $this->getCashAccount(); + $account = $this->getCashAccount(); $this->collector->setAccounts(new Collection()->push($account)); break; case '-account_is_cash': - $account = $this->getCashAccount(); + $account = $this->getCashAccount(); $this->collector->excludeAccounts(new Collection()->push($account)); break; - // - // description - // + // + // description + // case 'description_starts': $this->collector->descriptionStarts([$value]); @@ -710,7 +1682,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'description_contains': - $this->words[] = $value; + $this->words[] = $value; return false; @@ -729,11 +1701,11 @@ class OperatorQuerySearch implements SearchInterface break; - // - // currency - // + // + // currency + // case 'currency_is': - $currency = $this->findCurrency($value); + $currency = $this->findCurrency($value); if ($currency instanceof TransactionCurrency) { $this->collector->setCurrency($currency); } @@ -745,7 +1717,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-currency_is': - $currency = $this->findCurrency($value); + $currency = $this->findCurrency($value); if ($currency instanceof TransactionCurrency) { $this->collector->excludeCurrency($currency); } @@ -757,7 +1729,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'foreign_currency_is': - $currency = $this->findCurrency($value); + $currency = $this->findCurrency($value); if ($currency instanceof TransactionCurrency) { $this->collector->setForeignCurrency($currency); } @@ -769,7 +1741,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-foreign_currency_is': - $currency = $this->findCurrency($value); + $currency = $this->findCurrency($value); if ($currency instanceof TransactionCurrency) { $this->collector->excludeForeignCurrency($currency); } @@ -780,9 +1752,9 @@ class OperatorQuerySearch implements SearchInterface break; - // - // attachments - // + // + // attachments + // case 'has_attachments': case '-has_no_attachments': Log::debug('Set collector to filter on attachments.'); @@ -797,8 +1769,8 @@ class OperatorQuerySearch implements SearchInterface break; - // - // categories + // + // categories case '-has_any_category': case 'has_no_category': $this->collector->withoutCategory(); @@ -812,7 +1784,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'category_is': - $category = $this->categoryRepository->findByName($value); + $category = $this->categoryRepository->findByName($value); if (null !== $category) { $this->collector->setCategory($category); @@ -824,7 +1796,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-category_is': - $category = $this->categoryRepository->findByName($value); + $category = $this->categoryRepository->findByName($value); if (null !== $category) { $this->collector->excludeCategory($category); @@ -834,7 +1806,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'category_ends': - $result = $this->categoryRepository->categoryEndsWith($value, 1337); + $result = $this->categoryRepository->categoryEndsWith($value, 1337); if ($result->count() > 0) { $this->collector->setCategories($result); } @@ -846,7 +1818,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-category_ends': - $result = $this->categoryRepository->categoryEndsWith($value, 1337); + $result = $this->categoryRepository->categoryEndsWith($value, 1337); if ($result->count() > 0) { $this->collector->excludeCategories($result); } @@ -858,7 +1830,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'category_starts': - $result = $this->categoryRepository->categoryStartsWith($value, 1337); + $result = $this->categoryRepository->categoryStartsWith($value, 1337); if ($result->count() > 0) { $this->collector->setCategories($result); } @@ -870,7 +1842,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-category_starts': - $result = $this->categoryRepository->categoryStartsWith($value, 1337); + $result = $this->categoryRepository->categoryStartsWith($value, 1337); if ($result->count() > 0) { $this->collector->excludeCategories($result); } @@ -882,7 +1854,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'category_contains': - $result = $this->categoryRepository->searchCategory($value, 1337); + $result = $this->categoryRepository->searchCategory($value, 1337); if ($result->count() > 0) { $this->collector->setCategories($result); } @@ -894,7 +1866,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-category_contains': - $result = $this->categoryRepository->searchCategory($value, 1337); + $result = $this->categoryRepository->searchCategory($value, 1337); if ($result->count() > 0) { $this->collector->excludeCategories($result); } @@ -905,9 +1877,9 @@ class OperatorQuerySearch implements SearchInterface break; - // - // budgets - // + // + // budgets + // case '-has_any_budget': case 'has_no_budget': $this->collector->withoutBudget(); @@ -921,7 +1893,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'budget_contains': - $result = $this->budgetRepository->searchBudget($value, 1337); + $result = $this->budgetRepository->searchBudget($value, 1337); if ($result->count() > 0) { $this->collector->setBudgets($result); } @@ -933,7 +1905,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-budget_contains': - $result = $this->budgetRepository->searchBudget($value, 1337); + $result = $this->budgetRepository->searchBudget($value, 1337); if ($result->count() > 0) { $this->collector->excludeBudgets($result); } @@ -945,7 +1917,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'budget_is': - $budget = $this->budgetRepository->findByName($value); + $budget = $this->budgetRepository->findByName($value); if (null !== $budget) { $this->collector->setBudget($budget); @@ -957,7 +1929,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-budget_is': - $budget = $this->budgetRepository->findByName($value); + $budget = $this->budgetRepository->findByName($value); if (null !== $budget) { $this->collector->excludeBudget($budget); @@ -969,7 +1941,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'budget_ends': - $result = $this->budgetRepository->budgetEndsWith($value, 1337); + $result = $this->budgetRepository->budgetEndsWith($value, 1337); if ($result->count() > 0) { $this->collector->setBudgets($result); } @@ -981,7 +1953,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-budget_ends': - $result = $this->budgetRepository->budgetEndsWith($value, 1337); + $result = $this->budgetRepository->budgetEndsWith($value, 1337); if ($result->count() > 0) { $this->collector->excludeBudgets($result); } @@ -993,7 +1965,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'budget_starts': - $result = $this->budgetRepository->budgetStartsWith($value, 1337); + $result = $this->budgetRepository->budgetStartsWith($value, 1337); if ($result->count() > 0) { $this->collector->setBudgets($result); } @@ -1005,7 +1977,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-budget_starts': - $result = $this->budgetRepository->budgetStartsWith($value, 1337); + $result = $this->budgetRepository->budgetStartsWith($value, 1337); if ($result->count() > 0) { $this->collector->excludeBudgets($result); } @@ -1016,9 +1988,9 @@ class OperatorQuerySearch implements SearchInterface break; - // - // bill - // + // + // bill + // case '-has_any_bill': case 'has_no_bill': $this->collector->withoutBill(); @@ -1032,7 +2004,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'bill_contains': - $result = $this->billRepository->searchBill($value, 1337); + $result = $this->billRepository->searchBill($value, 1337); if ($result->count() > 0) { $this->collector->setBills($result); @@ -1044,7 +2016,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-bill_contains': - $result = $this->billRepository->searchBill($value, 1337); + $result = $this->billRepository->searchBill($value, 1337); if ($result->count() > 0) { $this->collector->excludeBills($result); @@ -1056,7 +2028,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'bill_is': - $bill = $this->billRepository->findByName($value); + $bill = $this->billRepository->findByName($value); if (null !== $bill) { $this->collector->setBill($bill); @@ -1068,7 +2040,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-bill_is': - $bill = $this->billRepository->findByName($value); + $bill = $this->billRepository->findByName($value); if (null !== $bill) { $this->collector->excludeBills(new Collection()->push($bill)); @@ -1080,7 +2052,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'bill_ends': - $result = $this->billRepository->billEndsWith($value, 1337); + $result = $this->billRepository->billEndsWith($value, 1337); if ($result->count() > 0) { $this->collector->setBills($result); } @@ -1092,7 +2064,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-bill_ends': - $result = $this->billRepository->billEndsWith($value, 1337); + $result = $this->billRepository->billEndsWith($value, 1337); if ($result->count() > 0) { $this->collector->excludeBills($result); } @@ -1104,7 +2076,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'bill_starts': - $result = $this->billRepository->billStartsWith($value, 1337); + $result = $this->billRepository->billStartsWith($value, 1337); if ($result->count() > 0) { $this->collector->setBills($result); } @@ -1116,7 +2088,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-bill_starts': - $result = $this->billRepository->billStartsWith($value, 1337); + $result = $this->billRepository->billStartsWith($value, 1337); if ($result->count() > 0) { $this->collector->excludeBills($result); } @@ -1127,9 +2099,9 @@ class OperatorQuerySearch implements SearchInterface break; - // - // tags - // + // + // tags + // case '-has_any_tag': case 'has_no_tag': $this->collector->withoutTags(); @@ -1144,7 +2116,7 @@ class OperatorQuerySearch implements SearchInterface case '-tag_is_not': case 'tag_is': - $result = $this->tagRepository->findByTag($value); + $result = $this->tagRepository->findByTag($value); if (null !== $result) { $this->includeTags[] = $result->id; $this->includeTags = array_unique($this->includeTags); @@ -1159,7 +2131,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'tag_contains': - $tags = $this->tagRepository->searchTag($value); + $tags = $this->tagRepository->searchTag($value); if (0 === $tags->count()) { Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); Log::warning(sprintf('Call to findNothing() from %s.', $operator)); @@ -1174,7 +2146,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'tag_starts': - $tags = $this->tagRepository->tagStartsWith($value); + $tags = $this->tagRepository->tagStartsWith($value); if (0 === $tags->count()) { Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); Log::warning(sprintf('Call to findNothing() from %s.', $operator)); @@ -1189,7 +2161,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-tag_starts': - $tags = $this->tagRepository->tagStartsWith($value); + $tags = $this->tagRepository->tagStartsWith($value); if (0 === $tags->count()) { Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); Log::warning(sprintf('Call to findNothing() from %s.', $operator)); @@ -1203,7 +2175,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'tag_ends': - $tags = $this->tagRepository->tagEndsWith($value); + $tags = $this->tagRepository->tagEndsWith($value); if (0 === $tags->count()) { Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); Log::warning(sprintf('Call to findNothing() from %s.', $operator)); @@ -1217,7 +2189,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-tag_ends': - $tags = $this->tagRepository->tagEndsWith($value); + $tags = $this->tagRepository->tagEndsWith($value); if (0 === $tags->count()) { Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); Log::warning(sprintf('Call to findNothing() from %s.', $operator)); @@ -1231,7 +2203,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-tag_contains': - $tags = $this->tagRepository->searchTag($value)->keyBy('id'); + $tags = $this->tagRepository->searchTag($value)->keyBy('id'); if (0 === $tags->count()) { Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); @@ -1247,7 +2219,7 @@ class OperatorQuerySearch implements SearchInterface case '-tag_is': case 'tag_is_not': - $result = $this->tagRepository->findByTag($value); + $result = $this->tagRepository->findByTag($value); if (null !== $result) { $this->excludeTags[] = $result->id; $this->excludeTags = array_unique($this->excludeTags); @@ -1255,9 +2227,9 @@ class OperatorQuerySearch implements SearchInterface break; - // - // notes - // + // + // notes + // case 'notes_contains': $this->collector->notesContain($value); @@ -1320,14 +2292,14 @@ class OperatorQuerySearch implements SearchInterface break; - // - // amount - // + // + // amount + // case 'amount_is': // strip comma's, make dots. Log::debug(sprintf('Original value "%s"', $value)); - $value = str_replace(',', '.', $value); - $amount = app('steam')->positive($value); + $value = str_replace(',', '.', $value); + $amount = app('steam')->positive($value); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->amountIs($amount); @@ -1336,8 +2308,8 @@ class OperatorQuerySearch implements SearchInterface case '-amount_is': // strip comma's, make dots. Log::debug(sprintf('Original value "%s"', $value)); - $value = str_replace(',', '.', $value); - $amount = app('steam')->positive($value); + $value = str_replace(',', '.', $value); + $amount = app('steam')->positive($value); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->amountIsNot($amount); @@ -1345,9 +2317,9 @@ class OperatorQuerySearch implements SearchInterface case 'foreign_amount_is': // strip comma's, make dots. - $value = str_replace(',', '.', $value); + $value = str_replace(',', '.', $value); - $amount = app('steam')->positive($value); + $amount = app('steam')->positive($value); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->foreignAmountIs($amount); @@ -1355,9 +2327,9 @@ class OperatorQuerySearch implements SearchInterface case '-foreign_amount_is': // strip comma's, make dots. - $value = str_replace(',', '.', $value); + $value = str_replace(',', '.', $value); - $amount = app('steam')->positive($value); + $amount = app('steam')->positive($value); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->foreignAmountIsNot($amount); @@ -1366,9 +2338,9 @@ class OperatorQuerySearch implements SearchInterface case '-amount_more': case 'amount_less': // strip comma's, make dots. - $value = str_replace(',', '.', $value); + $value = str_replace(',', '.', $value); - $amount = app('steam')->positive($value); + $amount = app('steam')->positive($value); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->amountLess($amount); @@ -1377,9 +2349,9 @@ class OperatorQuerySearch implements SearchInterface case '-foreign_amount_more': case 'foreign_amount_less': // strip comma's, make dots. - $value = str_replace(',', '.', $value); + $value = str_replace(',', '.', $value); - $amount = app('steam')->positive($value); + $amount = app('steam')->positive($value); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->foreignAmountLess($amount); @@ -1389,8 +2361,8 @@ class OperatorQuerySearch implements SearchInterface case 'amount_more': Log::debug(sprintf('Now handling operator "%s"', $operator)); // strip comma's, make dots. - $value = str_replace(',', '.', $value); - $amount = app('steam')->positive($value); + $value = str_replace(',', '.', $value); + $amount = app('steam')->positive($value); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->amountMore($amount); @@ -1400,16 +2372,16 @@ class OperatorQuerySearch implements SearchInterface case 'foreign_amount_more': Log::debug(sprintf('Now handling operator "%s"', $operator)); // strip comma's, make dots. - $value = str_replace(',', '.', $value); - $amount = app('steam')->positive($value); + $value = str_replace(',', '.', $value); + $amount = app('steam')->positive($value); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->foreignAmountMore($amount); break; - // - // transaction type - // + // + // transaction type + // case 'transaction_type': $this->collector->setTypes([ucfirst($value)]); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); @@ -1422,152 +2394,152 @@ class OperatorQuerySearch implements SearchInterface break; - // - // dates - // + // + // dates + // case '-date_on': case 'date_on': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setExactDateParams($range, $prohibited); return false; case 'date_before': case '-date_after': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setDateBeforeParams($range); return false; case 'date_after': case '-date_before': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setDateAfterParams($range); return false; case 'interest_date_on': case '-interest_date_on': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setExactMetaDateParams('interest_date', $range, $prohibited); return false; case 'interest_date_before': case '-interest_date_after': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateBeforeParams('interest_date', $range); return false; case 'interest_date_after': case '-interest_date_before': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateAfterParams('interest_date', $range); return false; case 'book_date_on': case '-book_date_on': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setExactMetaDateParams('book_date', $range, $prohibited); return false; case 'book_date_before': case '-book_date_after': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateBeforeParams('book_date', $range); return false; case 'book_date_after': case '-book_date_before': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateAfterParams('book_date', $range); return false; case 'process_date_on': case '-process_date_on': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setExactMetaDateParams('process_date', $range, $prohibited); return false; case 'process_date_before': case '-process_date_after': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateBeforeParams('process_date', $range); return false; case 'process_date_after': case '-process_date_before': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateAfterParams('process_date', $range); return false; case 'due_date_on': case '-due_date_on': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setExactMetaDateParams('due_date', $range, $prohibited); return false; case 'due_date_before': case '-due_date_after': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateBeforeParams('due_date', $range); return false; case 'due_date_after': case '-due_date_before': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateAfterParams('due_date', $range); return false; case 'payment_date_on': case '-payment_date_on': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setExactMetaDateParams('payment_date', $range, $prohibited); return false; case 'payment_date_before': case '-payment_date_after': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateBeforeParams('payment_date', $range); return false; case 'payment_date_after': case '-payment_date_before': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateAfterParams('payment_date', $range); return false; case 'invoice_date_on': case '-invoice_date_on': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setExactMetaDateParams('invoice_date', $range, $prohibited); return false; case 'invoice_date_before': case '-invoice_date_after': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateBeforeParams('invoice_date', $range); return false; case 'invoice_date_after': case '-invoice_date_before': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateAfterParams('invoice_date', $range); return false; @@ -1575,7 +2547,7 @@ class OperatorQuerySearch implements SearchInterface case 'created_at_on': case '-created_at_on': Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setExactObjectDateParams('created_at', $range, $prohibited); return false; @@ -1583,7 +2555,7 @@ class OperatorQuerySearch implements SearchInterface case 'created_at_before': case '-created_at_after': Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setObjectDateBeforeParams('created_at', $range); return false; @@ -1591,7 +2563,7 @@ class OperatorQuerySearch implements SearchInterface case 'created_at_after': case '-created_at_before': Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setObjectDateAfterParams('created_at', $range); return false; @@ -1599,7 +2571,7 @@ class OperatorQuerySearch implements SearchInterface case 'updated_at_on': case '-updated_at_on': Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setExactObjectDateParams('updated_at', $range, $prohibited); return false; @@ -1607,7 +2579,7 @@ class OperatorQuerySearch implements SearchInterface case 'updated_at_before': case '-updated_at_after': Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setObjectDateBeforeParams('updated_at', $range); return false; @@ -1615,14 +2587,14 @@ class OperatorQuerySearch implements SearchInterface case 'updated_at_after': case '-updated_at_before': Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setObjectDateAfterParams('updated_at', $range); return false; - // - // external URL - // + // + // external URL + // case '-any_external_url': case 'no_external_url': $this->collector->withoutExternalUrl(); @@ -1687,9 +2659,9 @@ class OperatorQuerySearch implements SearchInterface break; - // - // other fields - // + // + // other fields + // case 'external_id_is': $this->collector->setExternalId($value); @@ -1947,976 +2919,4 @@ class OperatorQuerySearch implements SearchInterface return true; } - - /** - * @throws FireflyException - */ - public static function getRootOperator(string $operator): string - { - $original = $operator; - // if the string starts with "-" (not), we can remove it and recycle - // the configuration from the original operator. - if (str_starts_with($operator, '-')) { - $operator = substr($operator, 1); - } - - $config = config(sprintf('search.operators.%s', $operator)); - if (null === $config) { - throw new FireflyException(sprintf('No configuration for search operator "%s"', $operator)); - } - if (true === $config['alias']) { - $return = $config['alias_for']; - if (str_starts_with($original, '-')) { - $return = sprintf('-%s', $config['alias_for']); - } - Log::debug(sprintf('"%s" is an alias for "%s", so return that instead.', $original, $return)); - - return $return; - } - Log::debug(sprintf('"%s" is not an alias.', $operator)); - - return $original; - } - - /** - * searchDirection: 1 = source (default), 2 = destination, 3 = both - * stringPosition: 1 = start (default), 2 = end, 3 = contains, 4 = is - * - * @SuppressWarnings("PHPMD.BooleanArgumentFlag") - * @SuppressWarnings("PHPMD.NPathComplexity") - */ - private function searchAccount(string $value, SearchDirection $searchDirection, StringPosition $stringPosition, bool $prohibited = false): void - { - Log::debug(sprintf('searchAccount("%s", %s, %s)', $value, $stringPosition->name, $searchDirection->name)); - - // search direction (default): for source accounts - $searchTypes = [AccountTypeEnum::ASSET->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::REVENUE->value]; - $collectorMethod = 'setSourceAccounts'; - if ($prohibited) { - $collectorMethod = 'excludeSourceAccounts'; - } - - // search direction: for destination accounts - if (SearchDirection::DESTINATION === $searchDirection) { // destination - // destination can be - $searchTypes = [AccountTypeEnum::ASSET->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::EXPENSE->value]; - $collectorMethod = 'setDestinationAccounts'; - if ($prohibited) { - $collectorMethod = 'excludeDestinationAccounts'; - } - } - // either account could be: - if (SearchDirection::BOTH === $searchDirection) { - $searchTypes = [AccountTypeEnum::ASSET->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::EXPENSE->value, AccountTypeEnum::REVENUE->value]; - $collectorMethod = 'setAccounts'; - if ($prohibited) { - $collectorMethod = 'excludeAccounts'; - } - } - // string position (default): starts with: - $stringMethod = 'str_starts_with'; - - // string position: ends with: - if (StringPosition::ENDS === $stringPosition) { - $stringMethod = 'str_ends_with'; - } - if (StringPosition::CONTAINS === $stringPosition) { - $stringMethod = 'str_contains'; - } - if (StringPosition::IS === $stringPosition) { - $stringMethod = 'stringIsEqual'; - } - - // get accounts: - $accounts = $this->accountRepository->searchAccount($value, $searchTypes, 1337); - if (0 === $accounts->count() && false === $prohibited) { - Log::warning('Found zero accounts, search for non existing account, NO results will be returned.'); - $this->collector->findNothing(); - - return; - } - if (0 === $accounts->count() && true === $prohibited) { - Log::debug('Found zero accounts, but the search is negated, so effectively we ignore the search parameter.'); - - return; - } - Log::debug(sprintf('Found %d accounts, will filter.', $accounts->count())); - $filtered = $accounts->filter( - static fn (Account $account) => $stringMethod(strtolower($account->name), strtolower($value)) - ); - - if (0 === $filtered->count()) { - Log::warning('Left with zero accounts, so cannot find anything, NO results will be returned.'); - $this->collector->findNothing(); - - return; - } - Log::debug(sprintf('Left with %d, set as %s().', $filtered->count(), $collectorMethod)); - $this->collector->{$collectorMethod}($filtered); // @phpstan-ignore-line - } - - /** - * TODO make enums - * searchDirection: 1 = source (default), 2 = destination, 3 = both - * stringPosition: 1 = start (default), 2 = end, 3 = contains, 4 = is - * - * @SuppressWarnings("PHPMD.BooleanArgumentFlag") - * @SuppressWarnings("PHPMD.NPathComplexity") - */ - private function searchAccountNr(string $value, SearchDirection $searchDirection, StringPosition $stringPosition, bool $prohibited = false): void - { - Log::debug(sprintf('searchAccountNr(%s, %d, %d)', $value, $searchDirection->name, $stringPosition->name)); - - // search direction (default): for source accounts - $searchTypes = [AccountTypeEnum::ASSET->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::REVENUE->value]; - $collectorMethod = 'setSourceAccounts'; - if (true === $prohibited) { - $collectorMethod = 'excludeSourceAccounts'; - } - - // search direction: for destination accounts - if (SearchDirection::DESTINATION === $searchDirection) { - // destination can be - $searchTypes = [AccountTypeEnum::ASSET->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::EXPENSE->value]; - $collectorMethod = 'setDestinationAccounts'; - if (true === $prohibited) { - $collectorMethod = 'excludeDestinationAccounts'; - } - } - - // either account could be: - if (SearchDirection::BOTH === $searchDirection) { - $searchTypes = [AccountTypeEnum::ASSET->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::EXPENSE->value, AccountTypeEnum::REVENUE->value]; - $collectorMethod = 'setAccounts'; - if (true === $prohibited) { - $collectorMethod = 'excludeAccounts'; - } - } - - // string position (default): starts with: - $stringMethod = 'str_starts_with'; - - // string position: ends with: - if (StringPosition::ENDS === $stringPosition) { - $stringMethod = 'str_ends_with'; - } - if (StringPosition::CONTAINS === $stringPosition) { - $stringMethod = 'str_contains'; - } - if (StringPosition::IS === $stringPosition) { - $stringMethod = 'stringIsEqual'; - } - - // search for accounts: - $accounts = $this->accountRepository->searchAccountNr($value, $searchTypes, 1337); - if (0 === $accounts->count()) { - Log::debug('Found zero accounts, search for invalid account.'); - Log::warning('Call to findNothing() from searchAccountNr().'); - $this->collector->findNothing(); - - return; - } - - // if found, do filter - Log::debug(sprintf('Found %d accounts, will filter.', $accounts->count())); - $filtered = $accounts->filter( - static function (Account $account) use ($value, $stringMethod) { - // either IBAN or account number - $ibanMatch = $stringMethod(strtolower((string) $account->iban), strtolower($value)); - $accountNrMatch = false; - - /** @var AccountMeta $meta */ - foreach ($account->accountMeta as $meta) { - if ('account_number' === $meta->name && $stringMethod(strtolower((string) $meta->data), strtolower($value))) { - $accountNrMatch = true; - } - } - - return $ibanMatch || $accountNrMatch; - } - ); - - if (0 === $filtered->count()) { - Log::debug('Left with zero, search for invalid account'); - Log::warning('Call to findNothing() from searchAccountNr().'); - $this->collector->findNothing(); - - return; - } - Log::debug(sprintf('Left with %d, set as %s().', $filtered->count(), $collectorMethod)); - $this->collector->{$collectorMethod}($filtered); // @phpstan-ignore-line - } - - private function getCashAccount(): Account - { - return $this->accountRepository->getCashAccount(); - } - - private function findCurrency(string $value): ?TransactionCurrency - { - if (str_contains($value, '(') && str_contains($value, ')')) { - // bad method to split and get the currency code: - $parts = explode(' ', $value); - $value = trim($parts[count($parts) - 1], "() \t\n\r\0\x0B"); - } - $result = $this->currencyRepository->findByCode($value); - if (null === $result) { - return $this->currencyRepository->findByName($value); - } - - return $result; - } - - /** - * @throws FireflyException - */ - private function parseDateRange(string $type, string $value): array - { - $parser = new ParseDateString(); - if ($parser->isDateRange($value)) { - return $parser->parseRange($value); - } - - try { - $parsedDate = $parser->parseDate($value); - } catch (FireflyException) { - Log::debug(sprintf('Could not parse date "%s", will return empty array.', $value)); - $this->invalidOperators[] = [ - 'type' => $type, - 'value' => $value, - ]; - - return []; - } - - return [ - 'exact' => $parsedDate, - ]; - } - - /** - * @throws FireflyException - * - * @SuppressWarnings("PHPMD.BooleanArgumentFlag") - */ - private function setExactDateParams(array $range, bool $prohibited = false): void - { - /** - * @var string $key - * @var Carbon|string $value - */ - foreach ($range as $key => $value) { - $key = $prohibited ? sprintf('%s_not', $key) : $key; - - switch ($key) { - default: - throw new FireflyException(sprintf('Cannot handle key "%s" in setExactParameters()', $key)); - - case 'exact': - if ($value instanceof Carbon) { - Log::debug(sprintf('Set date_is_exact value "%s"', $value->format('Y-m-d'))); - $this->collector->setRange($value, $value); - $this->operators->push(['type' => 'date_on', 'value' => $value->format('Y-m-d')]); - } - - break; - - case 'exact_not': - if ($value instanceof Carbon) { - $this->collector->excludeRange($value, $value); - $this->operators->push(['type' => 'not_date_on', 'value' => $value->format('Y-m-d')]); - } - - break; - - case 'year': - if (is_string($value)) { - Log::debug(sprintf('Set date_is_exact YEAR value "%s"', $value)); - $this->collector->yearIs($value); - $this->operators->push(['type' => 'date_on_year', 'value' => $value]); - } - - break; - - case 'year_not': - if (is_string($value)) { - Log::debug(sprintf('Set date_is_exact_not YEAR value "%s"', $value)); - $this->collector->yearIsNot($value); - $this->operators->push(['type' => 'not_date_on_year', 'value' => $value]); - } - - break; - - case 'month': - if (is_string($value)) { - Log::debug(sprintf('Set date_is_exact MONTH value "%s"', $value)); - $this->collector->monthIs($value); - $this->operators->push(['type' => 'date_on_month', 'value' => $value]); - } - - break; - - case 'month_not': - if (is_string($value)) { - Log::debug(sprintf('Set date_is_exact not MONTH value "%s"', $value)); - $this->collector->monthIsNot($value); - $this->operators->push(['type' => 'not_date_on_month', 'value' => $value]); - } - - break; - - case 'day': - if (is_string($value)) { - Log::debug(sprintf('Set date_is_exact DAY value "%s"', $value)); - $this->collector->dayIs($value); - $this->operators->push(['type' => 'date_on_day', 'value' => $value]); - } - - break; - - case 'day_not': - if (is_string($value)) { - Log::debug(sprintf('Set not date_is_exact DAY value "%s"', $value)); - $this->collector->dayIsNot($value); - $this->operators->push(['type' => 'not_date_on_day', 'value' => $value]); - } - - break; - } - } - } - - /** - * @throws FireflyException - * - * @SuppressWarnings("PHPMD.BooleanArgumentFlag") - */ - private function setDateBeforeParams(array $range, bool $prohibited = false): void - { - /** - * @var string $key - * @var Carbon|string $value - */ - foreach ($range as $key => $value) { - $key = $prohibited ? sprintf('%s_not', $key) : $key; - - switch ($key) { - default: - throw new FireflyException(sprintf('Cannot handle key "%s" in setDateBeforeParams()', $key)); - - case 'exact': - if ($value instanceof Carbon) { - $this->collector->setBefore($value); - $this->operators->push(['type' => 'date_before', 'value' => $value->format('Y-m-d')]); - } - - break; - - case 'year': - if (is_string($value)) { - Log::debug(sprintf('Set date_is_before YEAR value "%s"', $value)); - $this->collector->yearBefore($value); - $this->operators->push(['type' => 'date_before_year', 'value' => $value]); - } - - break; - - case 'month': - if (is_string($value)) { - Log::debug(sprintf('Set date_is_before MONTH value "%s"', $value)); - $this->collector->monthBefore($value); - $this->operators->push(['type' => 'date_before_month', 'value' => $value]); - } - - break; - - case 'day': - if (is_string($value)) { - Log::debug(sprintf('Set date_is_before DAY value "%s"', $value)); - $this->collector->dayBefore($value); - $this->operators->push(['type' => 'date_before_day', 'value' => $value]); - } - - break; - } - } - } - - /** - * @throws FireflyException - * - * @SuppressWarnings("PHPMD.BooleanArgumentFlag") - */ - private function setDateAfterParams(array $range, bool $prohibited = false): void - { - /** - * @var string $key - * @var Carbon|string $value - */ - foreach ($range as $key => $value) { - $key = $prohibited ? sprintf('%s_not', $key) : $key; - - switch ($key) { - default: - throw new FireflyException(sprintf('Cannot handle key "%s" in setDateAfterParams()', $key)); - - case 'exact': - if ($value instanceof Carbon) { - $this->collector->setAfter($value); - $this->operators->push(['type' => 'date_after', 'value' => $value->format('Y-m-d')]); - } - - break; - - case 'year': - if (is_string($value)) { - Log::debug(sprintf('Set date_is_after YEAR value "%s"', $value)); - $this->collector->yearAfter($value); - $this->operators->push(['type' => 'date_after_year', 'value' => $value]); - } - - break; - - case 'month': - if (is_string($value)) { - Log::debug(sprintf('Set date_is_after MONTH value "%s"', $value)); - $this->collector->monthAfter($value); - $this->operators->push(['type' => 'date_after_month', 'value' => $value]); - } - - break; - - case 'day': - if (is_string($value)) { - Log::debug(sprintf('Set date_is_after DAY value "%s"', $value)); - $this->collector->dayAfter($value); - $this->operators->push(['type' => 'date_after_day', 'value' => $value]); - } - - break; - } - } - } - - /** - * @throws FireflyException - * - * @SuppressWarnings("PHPMD.BooleanArgumentFlag") - */ - private function setExactMetaDateParams(string $field, array $range, bool $prohibited = false): void - { - Log::debug('Now in setExactMetaDateParams()'); - - /** - * @var string $key - * @var Carbon|string $value - */ - foreach ($range as $key => $value) { - $key = $prohibited ? sprintf('%s_not', $key) : $key; - - switch ($key) { - default: - throw new FireflyException(sprintf('Cannot handle key "%s" in setExactMetaDateParams()', $key)); - - case 'exact': - if ($value instanceof Carbon) { - Log::debug(sprintf('Set %s_is_exact value "%s"', $field, $value->format('Y-m-d'))); - $this->collector->setMetaDateRange($value, $value, $field); - $this->operators->push(['type' => sprintf('%s_on', $field), 'value' => $value->format('Y-m-d')]); - } - - break; - - case 'exact_not': - if ($value instanceof Carbon) { - Log::debug(sprintf('Set NOT %s_is_exact value "%s"', $field, $value->format('Y-m-d'))); - $this->collector->excludeMetaDateRange($value, $value, $field); - $this->operators->push(['type' => sprintf('not_%s_on', $field), 'value' => $value->format('Y-m-d')]); - } - - break; - - case 'year': - if (is_string($value)) { - Log::debug(sprintf('Set %s_is_exact YEAR value "%s"', $field, $value)); - $this->collector->metaYearIs($value, $field); - $this->operators->push(['type' => sprintf('%s_on_year', $field), 'value' => $value]); - } - - break; - - case 'year_not': - if (is_string($value)) { - Log::debug(sprintf('Set NOT %s_is_exact YEAR value "%s"', $field, $value)); - $this->collector->metaYearIsNot($value, $field); - $this->operators->push(['type' => sprintf('not_%s_on_year', $field), 'value' => $value]); - } - - break; - - case 'month': - if (is_string($value)) { - Log::debug(sprintf('Set %s_is_exact MONTH value "%s"', $field, $value)); - $this->collector->metaMonthIs($value, $field); - $this->operators->push(['type' => sprintf('%s_on_month', $field), 'value' => $value]); - } - - break; - - case 'month_not': - if (is_string($value)) { - Log::debug(sprintf('Set NOT %s_is_exact MONTH value "%s"', $field, $value)); - $this->collector->metaMonthIsNot($value, $field); - $this->operators->push(['type' => sprintf('not_%s_on_month', $field), 'value' => $value]); - } - - break; - - case 'day': - if (is_string($value)) { - Log::debug(sprintf('Set %s_is_exact DAY value "%s"', $field, $value)); - $this->collector->metaDayIs($value, $field); - $this->operators->push(['type' => sprintf('%s_on_day', $field), 'value' => $value]); - } - - break; - - case 'day_not': - if (is_string($value)) { - Log::debug(sprintf('Set NOT %s_is_exact DAY value "%s"', $field, $value)); - $this->collector->metaDayIsNot($value, $field); - $this->operators->push(['type' => sprintf('not_%s_on_day', $field), 'value' => $value]); - } - - break; - } - } - } - - /** - * @throws FireflyException - * - * @SuppressWarnings("PHPMD.BooleanArgumentFlag") - */ - private function setMetaDateBeforeParams(string $field, array $range, bool $prohibited = false): void - { - /** - * @var string $key - * @var Carbon|string $value - */ - foreach ($range as $key => $value) { - $key = $prohibited ? sprintf('%s_not', $key) : $key; - - switch ($key) { - default: - throw new FireflyException(sprintf('Cannot handle key "%s" in setMetaDateBeforeParams()', $key)); - - case 'exact': - if ($value instanceof Carbon) { - $this->collector->setMetaBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before', $field), 'value' => $value->format('Y-m-d')]); - } - - break; - - case 'year': - if (is_string($value)) { - Log::debug(sprintf('Set %s_is_before YEAR value "%s"', $field, $value)); - $this->collector->metaYearBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before_year', $field), 'value' => $value]); - } - - break; - - case 'month': - if (is_string($value)) { - Log::debug(sprintf('Set %s_is_before MONTH value "%s"', $field, $value)); - $this->collector->metaMonthBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before_month', $field), 'value' => $value]); - } - - break; - - case 'day': - if (is_string($value)) { - Log::debug(sprintf('Set %s_is_before DAY value "%s"', $field, $value)); - $this->collector->metaDayBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before_day', $field), 'value' => $value]); - } - - break; - } - } - } - - /** - * @throws FireflyException - * - * @SuppressWarnings("PHPMD.BooleanArgumentFlag") - */ - private function setMetaDateAfterParams(string $field, array $range, bool $prohibited = false): void - { - /** - * @var string $key - * @var Carbon|string $value - */ - foreach ($range as $key => $value) { - $key = $prohibited ? sprintf('%s_not', $key) : $key; - - switch ($key) { - default: - throw new FireflyException(sprintf('Cannot handle key "%s" in setMetaDateAfterParams()', $key)); - - case 'exact': - if ($value instanceof Carbon) { - $this->collector->setMetaAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after', $field), 'value' => $value->format('Y-m-d')]); - } - - break; - - case 'year': - if (is_string($value)) { - Log::debug(sprintf('Set %s_is_after YEAR value "%s"', $field, $value)); - $this->collector->metaYearAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after_year', $field), 'value' => $value]); - } - - break; - - case 'month': - if (is_string($value)) { - Log::debug(sprintf('Set %s_is_after MONTH value "%s"', $field, $value)); - $this->collector->metaMonthAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after_month', $field), 'value' => $value]); - } - - break; - - case 'day': - if (is_string($value)) { - Log::debug(sprintf('Set %s_is_after DAY value "%s"', $field, $value)); - $this->collector->metaDayAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after_day', $field), 'value' => $value]); - } - - break; - } - } - } - - /** - * @throws FireflyException - * - * @SuppressWarnings("PHPMD.BooleanArgumentFlag") - */ - private function setExactObjectDateParams(string $field, array $range, bool $prohibited = false): void - { - /** - * @var string $key - * @var Carbon|string $value - */ - foreach ($range as $key => $value) { - $key = $prohibited ? sprintf('%s_not', $key) : $key; - - switch ($key) { - default: - throw new FireflyException(sprintf('Cannot handle key "%s" in setExactObjectDateParams()', $key)); - - case 'exact': - if ($value instanceof Carbon) { - Log::debug(sprintf('Set %s_is_exact value "%s"', $field, $value->format('Y-m-d'))); - $this->collector->setObjectRange($value, clone $value, $field); - $this->operators->push(['type' => sprintf('%s_on', $field), 'value' => $value->format('Y-m-d')]); - } - - break; - - case 'exact_not': - if ($value instanceof Carbon) { - Log::debug(sprintf('Set NOT %s_is_exact value "%s"', $field, $value->format('Y-m-d'))); - $this->collector->excludeObjectRange($value, clone $value, $field); - $this->operators->push(['type' => sprintf('not_%s_on', $field), 'value' => $value->format('Y-m-d')]); - } - - break; - - case 'year': - if (is_string($value)) { - Log::debug(sprintf('Set %s_is_exact YEAR value "%s"', $field, $value)); - $this->collector->objectYearIs($value, $field); - $this->operators->push(['type' => sprintf('%s_on_year', $field), 'value' => $value]); - } - - break; - - case 'year_not': - if (is_string($value)) { - Log::debug(sprintf('Set NOT %s_is_exact YEAR value "%s"', $field, $value)); - $this->collector->objectYearIsNot($value, $field); - $this->operators->push(['type' => sprintf('not_%s_on_year', $field), 'value' => $value]); - } - - break; - - case 'month': - if (is_string($value)) { - Log::debug(sprintf('Set %s_is_exact MONTH value "%s"', $field, $value)); - $this->collector->objectMonthIs($value, $field); - $this->operators->push(['type' => sprintf('%s_on_month', $field), 'value' => $value]); - } - - break; - - case 'month_not': - if (is_string($value)) { - Log::debug(sprintf('Set NOT %s_is_exact MONTH value "%s"', $field, $value)); - $this->collector->objectMonthIsNot($value, $field); - $this->operators->push(['type' => sprintf('not_%s_on_month', $field), 'value' => $value]); - } - - break; - - case 'day': - if (is_string($value)) { - Log::debug(sprintf('Set %s_is_exact DAY value "%s"', $field, $value)); - $this->collector->objectDayIs($value, $field); - $this->operators->push(['type' => sprintf('%s_on_day', $field), 'value' => $value]); - } - - break; - - case 'day_not': - if (is_string($value)) { - Log::debug(sprintf('Set NOT %s_is_exact DAY value "%s"', $field, $value)); - $this->collector->objectDayIsNot($value, $field); - $this->operators->push(['type' => sprintf('not_%s_on_day', $field), 'value' => $value]); - } - - break; - } - } - } - - /** - * @throws FireflyException - * - * @SuppressWarnings("PHPMD.BooleanArgumentFlag") - */ - private function setObjectDateBeforeParams(string $field, array $range, bool $prohibited = false): void - { - /** - * @var string $key - * @var Carbon|string $value - */ - foreach ($range as $key => $value) { - $key = $prohibited ? sprintf('%s_not', $key) : $key; - - switch ($key) { - default: - throw new FireflyException(sprintf('Cannot handle key "%s" in setObjectDateBeforeParams()', $key)); - - case 'exact': - if ($value instanceof Carbon) { - $this->collector->setObjectBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before', $field), 'value' => $value->format('Y-m-d')]); - } - - break; - - case 'year': - if (is_string($value)) { - Log::debug(sprintf('Set date_is_before YEAR value "%s"', $value)); - $this->collector->objectYearBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before_year', $field), 'value' => $value]); - } - - break; - - case 'month': - if (is_string($value)) { - Log::debug(sprintf('Set date_is_before MONTH value "%s"', $value)); - $this->collector->objectMonthBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before_month', $field), 'value' => $value]); - } - - break; - - case 'day': - if (is_string($value)) { - Log::debug(sprintf('Set date_is_before DAY value "%s"', $value)); - $this->collector->objectDayBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before_day', $field), 'value' => $value]); - } - - break; - } - } - } - - /** - * @throws FireflyException - * - * @SuppressWarnings("PHPMD.BooleanArgumentFlag") - */ - private function setObjectDateAfterParams(string $field, array $range, bool $prohibited = false): void - { - /** - * @var string $key - * @var Carbon|string $value - */ - foreach ($range as $key => $value) { - $key = $prohibited ? sprintf('%s_not', $key) : $key; - - switch ($key) { - default: - throw new FireflyException(sprintf('Cannot handle key "%s" in setObjectDateAfterParams()', $key)); - - case 'exact': - if ($value instanceof Carbon) { - $this->collector->setObjectAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after', $field), 'value' => $value->format('Y-m-d')]); - } - - break; - - case 'year': - if (is_string($value)) { - Log::debug(sprintf('Set date_is_after YEAR value "%s"', $value)); - $this->collector->objectYearAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after_year', $field), 'value' => $value]); - } - - break; - - case 'month': - if (is_string($value)) { - Log::debug(sprintf('Set date_is_after MONTH value "%s"', $value)); - $this->collector->objectMonthAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after_month', $field), 'value' => $value]); - } - - break; - - case 'day': - if (is_string($value)) { - Log::debug(sprintf('Set date_is_after DAY value "%s"', $value)); - $this->collector->objectDayAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after_day', $field), 'value' => $value]); - } - - break; - } - } - } - - private function handleNodeGroup(NodeGroup $node, bool $flipProhibitedFlag): void - { - $prohibited = $node->isProhibited($flipProhibitedFlag); - - foreach ($node->getNodes() as $subNode) { - $this->handleSearchNode($subNode, $prohibited); - } - } - - public function searchTime(): float - { - return microtime(true) - $this->startTime; - } - - public function searchTransactions(): LengthAwarePaginator - { - $this->parseTagInstructions(); - if (0 === count($this->getWords()) && 0 === count($this->getExcludedWords()) && 0 === count($this->getOperators())) { - return new LengthAwarePaginator([], 0, 5, 1); - } - - return $this->collector->getPaginatedGroups(); - } - - private function parseTagInstructions(): void - { - Log::debug('Now in parseTagInstructions()'); - // if exclude tags, remove excluded tags. - if (count($this->excludeTags) > 0) { - Log::debug(sprintf('%d exclude tag(s)', count($this->excludeTags))); - $collection = new Collection(); - foreach ($this->excludeTags as $tagId) { - $tag = $this->tagRepository->find($tagId); - if (null !== $tag) { - Log::debug(sprintf('Exclude tag "%s"', $tag->tag)); - $collection->push($tag); - } - } - Log::debug(sprintf('Selecting all tags except %d excluded tag(s).', $collection->count())); - $this->collector->setWithoutSpecificTags($collection); - } - // if include tags, include them: - if (count($this->includeTags) > 0) { - Log::debug(sprintf('%d include tag(s)', count($this->includeTags))); - $collection = new Collection(); - foreach ($this->includeTags as $tagId) { - $tag = $this->tagRepository->find($tagId); - if (null !== $tag) { - Log::debug(sprintf('Include tag "%s"', $tag->tag)); - $collection->push($tag); - } - } - $this->collector->setAllTags($collection); - } - // if include ANY tags, include them: (see #8632) - if (count($this->includeAnyTags) > 0) { - Log::debug(sprintf('%d include ANY tag(s)', count($this->includeAnyTags))); - $collection = new Collection(); - foreach ($this->includeAnyTags as $tagId) { - $tag = $this->tagRepository->find($tagId); - if (null !== $tag) { - Log::debug(sprintf('Include ANY tag "%s"', $tag->tag)); - $collection->push($tag); - } - } - $this->collector->setTags($collection); - } - } - - public function getWords(): array - { - return $this->words; - } - - public function getExcludedWords(): array - { - return $this->prohibitedWords; - } - - public function setDate(Carbon $date): void - { - $this->date = $date; - } - - public function setPage(int $page): void - { - $this->page = $page; - $this->collector->setPage($this->page); - } - - public function setUser(User $user): void - { - $this->accountRepository->setUser($user); - $this->billRepository->setUser($user); - $this->categoryRepository->setUser($user); - $this->budgetRepository->setUser($user); - $this->tagRepository->setUser($user); - $this->collector = app(GroupCollectorInterface::class); - $this->collector->setUser($user); - $this->collector->withAccountInformation()->withCategoryInformation()->withBudgetInformation(); - - $this->setLimit((int) app('preferences')->getForUser($user, 'listPageSize', 50)->data); - } - - public function setLimit(int $limit): void - { - $this->limit = $limit; - $this->collector->setLimit($this->limit); - } } diff --git a/app/Support/Search/QueryParser/GdbotsQueryParser.php b/app/Support/Search/QueryParser/GdbotsQueryParser.php index a402013e48..0e670a21df 100644 --- a/app/Support/Search/QueryParser/GdbotsQueryParser.php +++ b/app/Support/Search/QueryParser/GdbotsQueryParser.php @@ -32,7 +32,6 @@ use Gdbots\QueryParser\QueryParser as BaseQueryParser; use Illuminate\Support\Facades\Log; use LogicException; use TypeError; - use function Safe\fwrite; class GdbotsQueryParser implements QueryParserInterface @@ -52,12 +51,12 @@ class GdbotsQueryParser implements QueryParserInterface try { $result = $this->parser->parse($query); $nodes = array_map( - fn (GdbotsNode\Node $node) => $this->convertNode($node), + fn(GdbotsNode\Node $node) => $this->convertNode($node), $result->getNodes() ); return new NodeGroup($nodes); - } catch (LogicException|TypeError $e) { + } catch (LogicException | TypeError $e) { fwrite(STDERR, "Setting up GdbotsQueryParserTest\n"); app('log')->error($e->getMessage()); app('log')->error(sprintf('Could not parse search: "%s".', $query)); @@ -76,7 +75,7 @@ class GdbotsQueryParser implements QueryParserInterface case $node instanceof GdbotsNode\Field: return new FieldNode( $node->getValue(), - (string) $node->getNode()->getValue(), + (string)$node->getNode()->getValue(), BoolOperator::PROHIBITED === $node->getBoolOperator() ); @@ -85,7 +84,7 @@ class GdbotsQueryParser implements QueryParserInterface return new NodeGroup( array_map( - fn (GdbotsNode\Node $subNode) => $this->convertNode($subNode), + fn(GdbotsNode\Node $subNode) => $this->convertNode($subNode), $node->getNodes() ) ); @@ -98,7 +97,7 @@ class GdbotsQueryParser implements QueryParserInterface case $node instanceof GdbotsNode\Mention: case $node instanceof GdbotsNode\Emoticon: case $node instanceof GdbotsNode\Emoji: - return new StringNode((string) $node->getValue(), BoolOperator::PROHIBITED === $node->getBoolOperator()); + return new StringNode((string)$node->getValue(), BoolOperator::PROHIBITED === $node->getBoolOperator()); default: throw new FireflyException( diff --git a/app/Support/Search/QueryParser/QueryParser.php b/app/Support/Search/QueryParser/QueryParser.php index c9072f970b..2533bcc3a8 100644 --- a/app/Support/Search/QueryParser/QueryParser.php +++ b/app/Support/Search/QueryParser/QueryParser.php @@ -46,22 +46,6 @@ class QueryParser implements QueryParserInterface return $this->buildNodeGroup(false); } - private function buildNodeGroup(bool $isSubquery, bool $prohibited = false): NodeGroup - { - $nodes = []; - $nodeResult = $this->buildNextNode($isSubquery); - - while ($nodeResult->node instanceof Node) { - $nodes[] = $nodeResult->node; - if ($nodeResult->isSubqueryEnd) { - break; - } - $nodeResult = $this->buildNextNode($isSubquery); - } - - return new NodeGroup($nodes, $prohibited); - } - private function buildNextNode(bool $isSubquery): NodeResult { $tokenUnderConstruction = ''; @@ -155,7 +139,7 @@ class QueryParser implements QueryParserInterface if ('' === $tokenUnderConstruction) { // In any other location, it's just a normal character $tokenUnderConstruction .= $char; - $skipNext = true; + $skipNext = true; } if ('' !== $tokenUnderConstruction && !$skipNext) { // @phpstan-ignore-line Log::debug(sprintf('Turns out that "%s" is a field name. Reset the token.', $tokenUnderConstruction)); @@ -187,13 +171,29 @@ class QueryParser implements QueryParserInterface ++$this->position; } - $finalNode = '' !== $tokenUnderConstruction || '' !== $fieldName + $finalNode = '' !== $tokenUnderConstruction || '' !== $fieldName ? $this->createNode($tokenUnderConstruction, $fieldName, $prohibited) : null; return new NodeResult($finalNode, true); } + private function buildNodeGroup(bool $isSubquery, bool $prohibited = false): NodeGroup + { + $nodes = []; + $nodeResult = $this->buildNextNode($isSubquery); + + while ($nodeResult->node instanceof Node) { + $nodes[] = $nodeResult->node; + if ($nodeResult->isSubqueryEnd) { + break; + } + $nodeResult = $this->buildNextNode($isSubquery); + } + + return new NodeGroup($nodes, $prohibited); + } + private function createNode(string $token, string $fieldName, bool $prohibited): Node { if ('' !== $fieldName) { diff --git a/app/Support/Singleton/PreferencesSingleton.php b/app/Support/Singleton/PreferencesSingleton.php index 32b9bb94f6..e8ff779c5e 100644 --- a/app/Support/Singleton/PreferencesSingleton.php +++ b/app/Support/Singleton/PreferencesSingleton.php @@ -29,7 +29,7 @@ class PreferencesSingleton { private static ?PreferencesSingleton $instance = null; - private array $preferences = []; + private array $preferences = []; private function __construct() { @@ -45,6 +45,11 @@ class PreferencesSingleton return self::$instance; } + public function getPreference(string $key): mixed + { + return $this->preferences[$key] ?? null; + } + public function resetPreferences(): void { $this->preferences = []; @@ -54,9 +59,4 @@ class PreferencesSingleton { $this->preferences[$key] = $value; } - - public function getPreference(string $key): mixed - { - return $this->preferences[$key] ?? null; - } } diff --git a/app/Support/Steam.php b/app/Support/Steam.php index e103f19ece..c9a13be8b7 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -38,7 +38,6 @@ use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; use ValueError; - use function Safe\parse_url; use function Safe\preg_replace; @@ -47,6 +46,81 @@ use function Safe\preg_replace; */ class Steam { + public function accountsBalancesOptimized(Collection $accounts, Carbon $date, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null): array + { + Log::debug(sprintf('accountsBalancesOptimized: Called for %d account(s) with date/time "%s"', $accounts->count(), $date->toIso8601String())); + $result = []; + $convertToPrimary ??= Amount::convertToPrimary(); + $primary ??= Amount::getPrimaryCurrency(); + $currencies = $this->getCurrencies($accounts); + + // balance(s) in all currencies for ALL accounts. + $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')) + ->groupBy(['transactions.account_id', 'transaction_currencies.code']) + ->get(['transactions.account_id', 'transaction_currencies.code', DB::raw('SUM(transactions.amount) as sum_of_amount')])->toArray(); + + /** @var Account $account */ + foreach ($accounts as $account) { + // this array is PER account, so we wait a bit before we change code here. + $return = [ + 'pc_balance' => '0', + 'balance' => '0', // this key is overwritten right away, but I must remember it is always created. + ]; + $currency = $currencies[$account->id]; + + // second array + $accountSum = array_filter($arrayOfSums, fn($entry) => $entry['account_id'] === $account->id); + if (0 === count($accountSum)) { + $result[$account->id] = $return; + + continue; + } + $accountSum = array_values($accountSum)[0]; + $sumOfAmount = (string)$accountSum['sum_of_amount']; + $sumOfAmount = $this->floatalize('' === $sumOfAmount ? '0' : $sumOfAmount); + $sumsByCode = [ + $accountSum['code'] => $sumOfAmount, + ]; + + // Log::debug('All balances are (joined)', $others); + // if there is no request to convert, take this as "balance" and "pc_balance". + $return['balance'] = $sumsByCode[$currency->code] ?? '0'; + if (!$convertToPrimary) { + unset($return['pc_balance']); + // Log::debug(sprintf('Set balance to %s, unset pc_balance', $return['balance'])); + } + // if there is a request to convert, convert to "pc_balance" and use "balance" for whichever amount is in the primary currency. + if ($convertToPrimary) { + $return['pc_balance'] = $this->convertAllBalances($sumsByCode, $primary, $date); + // Log::debug(sprintf('Set pc_balance to %s', $return['pc_balance'])); + } + + // either way, the balance is always combined with the virtual balance: + $virtualBalance = (string)('' === (string)$account->virtual_balance ? '0' : $account->virtual_balance); + + if ($convertToPrimary) { + // the primary currency balance is combined with a converted virtual_balance: + $converter = new ExchangeRateConverter(); + $pcVirtualBalance = $converter->convert($currency, $primary, $date, $virtualBalance); + $return['pc_balance'] = bcadd($pcVirtualBalance, $return['pc_balance']); + // Log::debug(sprintf('Primary virtual balance makes the primary total %s', $return['pc_balance'])); + } + if (!$convertToPrimary) { + // if not, also increase the balance + primary balance for consistency. + $return['balance'] = bcadd($return['balance'], $virtualBalance); + // Log::debug(sprintf('Virtual balance makes the (primary currency) total %s', $return['balance'])); + } + $final = array_merge($return, $sumsByCode); + $result[$account->id] = $final; + // Log::debug('Final balance is', $final); + } + + return $result; + } + /** * https://stackoverflow.com/questions/1642614/how-to-ceil-floor-and-round-bcmath-numbers */ @@ -66,27 +140,15 @@ class Steam // Log::debug(sprintf('Trying bcround("%s",%d)', $number, $precision)); if (str_contains($number, '.')) { if ('-' !== $number[0]) { - return bcadd($number, '0.'.str_repeat('0', $precision).'5', $precision); + return bcadd($number, '0.' . str_repeat('0', $precision) . '5', $precision); } - return bcsub($number, '0.'.str_repeat('0', $precision).'5', $precision); + return bcsub($number, '0.' . str_repeat('0', $precision) . '5', $precision); } return $number; } - public function filterAccountBalances(array $total, Account $account, bool $convertToPrimary, ?TransactionCurrency $currency = null): array - { - Log::debug(sprintf('filterAccountBalances(#%d)', $account->id)); - $return = []; - foreach ($total as $key => $value) { - $return[$key] = $this->filterAccountBalance($value, $account, $convertToPrimary, $currency); - } - Log::debug(sprintf('end of filterAccountBalances(#%d)', $account->id)); - - return $return; - } - public function filterAccountBalance(array $set, Account $account, bool $convertToPrimary, ?TransactionCurrency $currency = null): array { Log::debug(sprintf('filterAccountBalance(#%d)', $account->id), $set); @@ -138,6 +200,18 @@ class Steam return $set; } + public function filterAccountBalances(array $total, Account $account, bool $convertToPrimary, ?TransactionCurrency $currency = null): array + { + Log::debug(sprintf('filterAccountBalances(#%d)', $account->id)); + $return = []; + foreach ($total as $key => $value) { + $return[$key] = $this->filterAccountBalance($value, $account, $convertToPrimary, $currency); + } + Log::debug(sprintf('end of filterAccountBalances(#%d)', $account->id)); + + return $return; + } + public function filterSpaces(string $string): string { $search = [ @@ -197,6 +271,94 @@ class Steam return str_replace($search, '', $string); } + /** + * Returns smaller than or equal to, so be careful with END OF DAY. + * + * Returns the balance of an account at exact moment given. Array with at least one value. + * Always returns: + * "balance": balance in the account's currency OR user's primary currency if the account has no currency + * "EUR": balance in EUR (or whatever currencies the account has balance in) + * + * If the user has $convertToPrimary: + * "balance": balance in the account's currency OR user's primary currency if the account has no currency + * --> "pc_balance": balance in the user's primary currency, with all amounts converted to the primary currency. + * "EUR": balance in EUR (or whatever currencies the account has balance in) + */ + public function finalAccountBalance(Account $account, Carbon $date, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null): array + { + + $cache = new CacheProperties(); + $cache->addProperty($account->id); + $cache->addProperty($date); + if ($cache->has()) { + Log::debug(sprintf('CACHED finalAccountBalance(#%d, %s)', $account->id, $date->format('Y-m-d H:i:s'))); + + // return $cache->get(); + } + // Log::debug(sprintf('finalAccountBalance(#%d, %s)', $account->id, $date->format('Y-m-d H:i:s'))); + if (null === $convertToPrimary) { + $convertToPrimary = Amount::convertToPrimary($account->user); + } + if (!$primary instanceof TransactionCurrency) { + $primary = Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup); + } + // account balance thing. + $currencyPresent = isset($account->meta) && array_key_exists('currency', $account->meta) && null !== $account->meta['currency']; + if ($currencyPresent) { + $accountCurrency = $account->meta['currency']; + } + if (!$currencyPresent) { + + $accountCurrency = $this->getAccountCurrency($account); + } + $hasCurrency = null !== $accountCurrency; + $currency = $hasCurrency ? $accountCurrency : $primary; + $return = [ + 'pc_balance' => '0', + 'balance' => '0', // this key is overwritten right away, but I must remember it is always created. + ]; + // balance(s) in all currencies. + $array = $account->transactions() + ->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')) + ->get(['transaction_currencies.code', 'transactions.amount'])->toArray(); + $others = $this->groupAndSumTransactions($array, 'code', 'amount'); + // Log::debug('All balances are (joined)', $others); + // if there is no request to convert, take this as "balance" and "pc_balance". + $return['balance'] = $others[$currency->code] ?? '0'; + if (!$convertToPrimary) { + unset($return['pc_balance']); + // Log::debug(sprintf('Set balance to %s, unset pc_balance', $return['balance'])); + } + // if there is a request to convert, convert to "pc_balance" and use "balance" for whichever amount is in the primary currency. + if ($convertToPrimary) { + $return['pc_balance'] = $this->convertAllBalances($others, $primary, $date); // todo sum all and convert. + // Log::debug(sprintf('Set pc_balance to %s', $return['pc_balance'])); + } + + // either way, the balance is always combined with the virtual balance: + $virtualBalance = (string)('' === (string)$account->virtual_balance ? '0' : $account->virtual_balance); + + if ($convertToPrimary) { + // the primary currency balance is combined with a converted virtual_balance: + $converter = new ExchangeRateConverter(); + $pcVirtualBalance = $converter->convert($currency, $primary, $date, $virtualBalance); + $return['pc_balance'] = bcadd($pcVirtualBalance, $return['pc_balance']); + // Log::debug(sprintf('Primary virtual balance makes the primary total %s', $return['pc_balance'])); + } + if (!$convertToPrimary) { + // if not, also increase the balance + primary balance for consistency. + $return['balance'] = bcadd($return['balance'], $virtualBalance); + // Log::debug(sprintf('Virtual balance makes the (primary currency) total %s', $return['balance'])); + } + $final = array_merge($return, $others); + // Log::debug('Final balance is', $final); + $cache->store($final); + + return $final; + } + public function finalAccountBalanceInRange(Account $account, Carbon $start, Carbon $end, bool $convertToPrimary): array { // expand period. @@ -205,7 +367,7 @@ class Steam Log::debug(sprintf('finalAccountBalanceInRange(#%d, %s, %s)', $account->id, $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); // set up cache - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($account->id); $cache->addProperty('final-balance-in-range'); $cache->addProperty($start); @@ -215,22 +377,22 @@ class Steam return $cache->get(); } - $balances = []; - $formatted = $start->format('Y-m-d'); + $balances = []; + $formatted = $start->format('Y-m-d'); /* * To make sure the start balance is correct, we need to get the balance at the exact end of the previous day. * Since we just did "startOfDay" we can do subDay()->endOfDay() to get the correct moment. * THAT will be the start balance. */ - $request = clone $start; + $request = clone $start; $request->subDay()->endOfDay(); Log::debug('Get first balance to start.'); Log::debug(sprintf('finalAccountBalanceInRange: Call finalAccountBalance with date/time "%s"', $request->toIso8601String())); - $startBalance = $this->finalAccountBalance($account, $request); - $primaryCurrency = Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup); - $accountCurrency = $this->getAccountCurrency($account); - $hasCurrency = $accountCurrency instanceof TransactionCurrency; - $currency = $accountCurrency ?? $primaryCurrency; + $startBalance = $this->finalAccountBalance($account, $request); + $primaryCurrency = Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup); + $accountCurrency = $this->getAccountCurrency($account); + $hasCurrency = $accountCurrency instanceof TransactionCurrency; + $currency = $accountCurrency ?? $primaryCurrency; Log::debug(sprintf('Currency is %s', $currency->code)); @@ -243,7 +405,7 @@ class Steam Log::debug(sprintf('Also set start balance in %s', $primaryCurrency->code)); $startBalance[$primaryCurrency->code] ??= '0'; } - $currencies = [ + $currencies = [ $currency->id => $currency, $primaryCurrency->id => $primaryCurrency, ]; @@ -253,48 +415,47 @@ class Steam // sums up the balance changes per day. Log::debug(sprintf('Date >= %s and <= %s', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); - $set = $account->transactions() - ->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->where('transaction_journals.date', '>=', $start->format('Y-m-d H:i:s')) - ->where('transaction_journals.date', '<=', $end->format('Y-m-d H:i:s')) - ->groupBy('transaction_journals.date') - ->groupBy('transactions.transaction_currency_id') - ->orderBy('transaction_journals.date', 'ASC') - ->whereNull('transaction_journals.deleted_at') - ->get( - [ // @phpstan-ignore-line - 'transaction_journals.date', - 'transactions.transaction_currency_id', - DB::raw('SUM(transactions.amount) AS sum_of_day'), - ] - ) - ; + $set = $account->transactions() + ->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->where('transaction_journals.date', '>=', $start->format('Y-m-d H:i:s')) + ->where('transaction_journals.date', '<=', $end->format('Y-m-d H:i:s')) + ->groupBy('transaction_journals.date') + ->groupBy('transactions.transaction_currency_id') + ->orderBy('transaction_journals.date', 'ASC') + ->whereNull('transaction_journals.deleted_at') + ->get( + [ // @phpstan-ignore-line + 'transaction_journals.date', + 'transactions.transaction_currency_id', + DB::raw('SUM(transactions.amount) AS sum_of_day'), + ] + ); - $currentBalance = $startBalance; - $converter = new ExchangeRateConverter(); + $currentBalance = $startBalance; + $converter = new ExchangeRateConverter(); /** @var Transaction $entry */ foreach ($set as $entry) { // get date object - $carbon = new Carbon($entry->date, $entry->date_tz); - $carbonKey = $carbon->format('Y-m-d'); + $carbon = new Carbon($entry->date, $entry->date_tz); + $carbonKey = $carbon->format('Y-m-d'); // make sure sum is a string: - $sumOfDay = (string)($entry->sum_of_day ?? '0'); + $sumOfDay = (string)($entry->sum_of_day ?? '0'); // #10426 make sure sum is not in scientific notation. - $sumOfDay = $this->floatalize($sumOfDay); + $sumOfDay = $this->floatalize($sumOfDay); // find currency of this entry, does not have to exist. $currencies[$entry->transaction_currency_id] ??= Amount::getTransactionCurrencyById($entry->transaction_currency_id); // make sure this $entry has its own $entryCurrency /** @var TransactionCurrency $entryCurrency */ - $entryCurrency = $currencies[$entry->transaction_currency_id]; + $entryCurrency = $currencies[$entry->transaction_currency_id]; Log::debug(sprintf('Processing transaction(s) on moment %s', $carbon->format('Y-m-d H:i:s'))); // add amount to current balance in currency code. - $currentBalance[$entryCurrency->code] ??= '0'; + $currentBalance[$entryCurrency->code] ??= '0'; $currentBalance[$entryCurrency->code] = bcadd($sumOfDay, (string)$currentBalance[$entryCurrency->code]); // if not requested to convert to primary currency, add the amount to "balance", do nothing else. @@ -312,7 +473,7 @@ class Steam } } // add to final array. - $balances[$carbonKey] = $currentBalance; + $balances[$carbonKey] = $currentBalance; Log::debug(sprintf('Updated entry [%s]', $carbonKey), $currentBalance); } $cache->store($balances); @@ -321,175 +482,40 @@ class Steam return $balances; } - public function accountsBalancesOptimized(Collection $accounts, Carbon $date, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null): array - { - Log::debug(sprintf('accountsBalancesOptimized: Called for %d account(s) with date/time "%s"', $accounts->count(), $date->toIso8601String())); - $result = []; - $convertToPrimary ??= Amount::convertToPrimary(); - $primary ??= Amount::getPrimaryCurrency(); - $currencies = $this->getCurrencies($accounts); - - // balance(s) in all currencies for ALL accounts. - $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')) - ->groupBy(['transactions.account_id', 'transaction_currencies.code']) - ->get(['transactions.account_id', 'transaction_currencies.code', DB::raw('SUM(transactions.amount) as sum_of_amount')])->toArray() - ; - - /** @var Account $account */ - foreach ($accounts as $account) { - // this array is PER account, so we wait a bit before we change code here. - $return = [ - 'pc_balance' => '0', - 'balance' => '0', // this key is overwritten right away, but I must remember it is always created. - ]; - $currency = $currencies[$account->id]; - - // second array - $accountSum = array_filter($arrayOfSums, fn ($entry) => $entry['account_id'] === $account->id); - if (0 === count($accountSum)) { - $result[$account->id] = $return; - - continue; - } - $accountSum = array_values($accountSum)[0]; - $sumOfAmount = (string)$accountSum['sum_of_amount']; - $sumOfAmount = $this->floatalize('' === $sumOfAmount ? '0' : $sumOfAmount); - $sumsByCode = [ - $accountSum['code'] => $sumOfAmount, - ]; - - // Log::debug('All balances are (joined)', $others); - // if there is no request to convert, take this as "balance" and "pc_balance". - $return['balance'] = $sumsByCode[$currency->code] ?? '0'; - if (!$convertToPrimary) { - unset($return['pc_balance']); - // Log::debug(sprintf('Set balance to %s, unset pc_balance', $return['balance'])); - } - // if there is a request to convert, convert to "pc_balance" and use "balance" for whichever amount is in the primary currency. - if ($convertToPrimary) { - $return['pc_balance'] = $this->convertAllBalances($sumsByCode, $primary, $date); - // Log::debug(sprintf('Set pc_balance to %s', $return['pc_balance'])); - } - - // either way, the balance is always combined with the virtual balance: - $virtualBalance = (string)('' === (string)$account->virtual_balance ? '0' : $account->virtual_balance); - - if ($convertToPrimary) { - // the primary currency balance is combined with a converted virtual_balance: - $converter = new ExchangeRateConverter(); - $pcVirtualBalance = $converter->convert($currency, $primary, $date, $virtualBalance); - $return['pc_balance'] = bcadd($pcVirtualBalance, $return['pc_balance']); - // Log::debug(sprintf('Primary virtual balance makes the primary total %s', $return['pc_balance'])); - } - if (!$convertToPrimary) { - // if not, also increase the balance + primary balance for consistency. - $return['balance'] = bcadd($return['balance'], $virtualBalance); - // Log::debug(sprintf('Virtual balance makes the (primary currency) total %s', $return['balance'])); - } - $final = array_merge($return, $sumsByCode); - $result[$account->id] = $final; - // Log::debug('Final balance is', $final); - } - - return $result; - } - /** - * Returns smaller than or equal to, so be careful with END OF DAY. + * https://framework.zend.com/downloads/archives * - * Returns the balance of an account at exact moment given. Array with at least one value. - * Always returns: - * "balance": balance in the account's currency OR user's primary currency if the account has no currency - * "EUR": balance in EUR (or whatever currencies the account has balance in) - * - * If the user has $convertToPrimary: - * "balance": balance in the account's currency OR user's primary currency if the account has no currency - * --> "pc_balance": balance in the user's primary currency, with all amounts converted to the primary currency. - * "EUR": balance in EUR (or whatever currencies the account has balance in) + * Convert a scientific notation to float + * Additionally fixed a problem with PHP <= 5.2.x with big integers */ - public function finalAccountBalance(Account $account, Carbon $date, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null): array + public function floatalize(string $value): string { + $value = strtoupper($value); + if (!str_contains($value, 'E')) { + return $value; + } + Log::debug(sprintf('Floatalizing %s', $value)); - $cache = new CacheProperties(); - $cache->addProperty($account->id); - $cache->addProperty($date); - if ($cache->has()) { - Log::debug(sprintf('CACHED finalAccountBalance(#%d, %s)', $account->id, $date->format('Y-m-d H:i:s'))); + $number = substr($value, 0, (int)strpos($value, 'E')); + if (str_contains($number, '.')) { + $post = strlen(substr($number, (int)strpos($number, '.') + 1)); + $mantis = substr($value, (int)strpos($value, 'E') + 1); + if ($mantis < 0) { + $post += abs((int)$mantis); + } - // return $cache->get(); - } - // Log::debug(sprintf('finalAccountBalance(#%d, %s)', $account->id, $date->format('Y-m-d H:i:s'))); - if (null === $convertToPrimary) { - $convertToPrimary = Amount::convertToPrimary($account->user); - } - if (!$primary instanceof TransactionCurrency) { - $primary = Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup); - } - // account balance thing. - $currencyPresent = isset($account->meta) && array_key_exists('currency', $account->meta) && null !== $account->meta['currency']; - if ($currencyPresent) { - $accountCurrency = $account->meta['currency']; - } - if (!$currencyPresent) { - - $accountCurrency = $this->getAccountCurrency($account); - } - $hasCurrency = null !== $accountCurrency; - $currency = $hasCurrency ? $accountCurrency : $primary; - $return = [ - 'pc_balance' => '0', - 'balance' => '0', // this key is overwritten right away, but I must remember it is always created. - ]; - // balance(s) in all currencies. - $array = $account->transactions() - ->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')) - ->get(['transaction_currencies.code', 'transactions.amount'])->toArray() - ; - $others = $this->groupAndSumTransactions($array, 'code', 'amount'); - // Log::debug('All balances are (joined)', $others); - // if there is no request to convert, take this as "balance" and "pc_balance". - $return['balance'] = $others[$currency->code] ?? '0'; - if (!$convertToPrimary) { - unset($return['pc_balance']); - // Log::debug(sprintf('Set balance to %s, unset pc_balance', $return['balance'])); - } - // if there is a request to convert, convert to "pc_balance" and use "balance" for whichever amount is in the primary currency. - if ($convertToPrimary) { - $return['pc_balance'] = $this->convertAllBalances($others, $primary, $date); // todo sum all and convert. - // Log::debug(sprintf('Set pc_balance to %s', $return['pc_balance'])); + // TODO careless float could break financial math. + return number_format((float)$value, $post, '.', ''); } - // either way, the balance is always combined with the virtual balance: - $virtualBalance = (string)('' === (string)$account->virtual_balance ? '0' : $account->virtual_balance); - - if ($convertToPrimary) { - // the primary currency balance is combined with a converted virtual_balance: - $converter = new ExchangeRateConverter(); - $pcVirtualBalance = $converter->convert($currency, $primary, $date, $virtualBalance); - $return['pc_balance'] = bcadd($pcVirtualBalance, $return['pc_balance']); - // Log::debug(sprintf('Primary virtual balance makes the primary total %s', $return['pc_balance'])); - } - if (!$convertToPrimary) { - // if not, also increase the balance + primary balance for consistency. - $return['balance'] = bcadd($return['balance'], $virtualBalance); - // Log::debug(sprintf('Virtual balance makes the (primary currency) total %s', $return['balance'])); - } - $final = array_merge($return, $others); - // Log::debug('Final balance is', $final); - $cache->store($final); - - return $final; + // TODO careless float could break financial math. + return number_format((float)$value, 0, '.', ''); } public function getAccountCurrency(Account $account): ?TransactionCurrency { - $type = $account->accountType->type; - $list = config('firefly.valid_currency_account_types'); + $type = $account->accountType->type; + $list = config('firefly.valid_currency_account_types'); // return null if not in this list. if (!in_array($type, $list, true)) { @@ -503,45 +529,6 @@ class Steam return Amount::getTransactionCurrencyById((int)$result->data); } - private function groupAndSumTransactions(array $array, string $group, string $field): array - { - $return = []; - - foreach ($array as $item) { - $groupKey = $item[$group] ?? 'unknown'; - $return[$groupKey] = bcadd($return[$groupKey] ?? '0', (string)$item[$field]); - } - - return $return; - } - - private function convertAllBalances(array $others, TransactionCurrency $primary, Carbon $date): string - { - $total = '0'; - $converter = new ExchangeRateConverter(); - $singleton = PreferencesSingleton::getInstance(); - foreach ($others as $key => $amount) { - $preference = $singleton->getPreference($key); - - try { - $currency = $preference ?? Amount::getTransactionCurrencyByCode($key); - } catch (FireflyException) { - continue; - } - if (null === $preference) { - $singleton->setPreference($key, $currency); - } - $current = $amount; - if ($currency->id !== $primary->id) { - $current = $converter->convert($currency, $primary, $date, $amount); - Log::debug(sprintf('Convert %s %s to %s %s', $currency->code, $amount, $primary->code, $current)); - } - $total = bcadd($current, $total); - } - - return $total; - } - /** * @throws FireflyException */ @@ -563,19 +550,34 @@ class Steam return (string)$host; } + /** + * Get user's language. + * + * @throws FireflyException + */ + public function getLanguage(): string // get preference + { + $preference = app('preferences')->get('language', config('firefly.default_language', 'en_US'))->data; + if (!is_string($preference)) { + throw new FireflyException(sprintf('Preference "language" must be a string, but is unexpectedly a "%s".', gettype($preference))); + } + + return str_replace('-', '_', $preference); + } + public function getLastActivities(array $accounts): array { $list = []; - $set = auth()->user()->transactions() - ->whereIn('transactions.account_id', $accounts) - ->groupBy(['transactions.account_id', 'transaction_journals.user_id']) - ->get(['transactions.account_id', DB::raw('MAX(transaction_journals.date) AS max_date')]) // @phpstan-ignore-line + $set = auth()->user()->transactions() + ->whereIn('transactions.account_id', $accounts) + ->groupBy(['transactions.account_id', 'transaction_journals.user_id']) + ->get(['transactions.account_id', DB::raw('MAX(transaction_journals.date) AS max_date')]) // @phpstan-ignore-line ; /** @var Transaction $entry */ foreach ($set as $entry) { - $date = new Carbon($entry->max_date, config('app.timezone')); + $date = new Carbon($entry->max_date, config('app.timezone')); $date->setTimezone(config('app.timezone')); $list[(int)$entry->account_id] = $date; } @@ -605,21 +607,6 @@ class Steam return $locale; } - /** - * Get user's language. - * - * @throws FireflyException - */ - public function getLanguage(): string // get preference - { - $preference = app('preferences')->get('language', config('firefly.default_language', 'en_US'))->data; - if (!is_string($preference)) { - throw new FireflyException(sprintf('Preference "language" must be a string, but is unexpectedly a "%s".', gettype($preference))); - } - - return str_replace('-', '_', $preference); - } - public function getLocaleArray(string $locale): array { return [ @@ -650,9 +637,9 @@ class Steam public function getSafeUrl(string $unknownUrl, string $safeUrl): string { // Log::debug(sprintf('getSafeUrl(%s, %s)', $unknownUrl, $safeUrl)); - $returnUrl = $safeUrl; - $unknownHost = parse_url($unknownUrl, PHP_URL_HOST); - $safeHost = parse_url($safeUrl, PHP_URL_HOST); + $returnUrl = $safeUrl; + $unknownHost = parse_url($unknownUrl, PHP_URL_HOST); + $safeHost = parse_url($safeUrl, PHP_URL_HOST); if (null !== $unknownHost && $unknownHost === $safeHost) { $returnUrl = $unknownUrl; @@ -681,36 +668,6 @@ class Steam return $amount; } - /** - * https://framework.zend.com/downloads/archives - * - * Convert a scientific notation to float - * Additionally fixed a problem with PHP <= 5.2.x with big integers - */ - public function floatalize(string $value): string - { - $value = strtoupper($value); - if (!str_contains($value, 'E')) { - return $value; - } - Log::debug(sprintf('Floatalizing %s', $value)); - - $number = substr($value, 0, (int)strpos($value, 'E')); - if (str_contains($number, '.')) { - $post = strlen(substr($number, (int)strpos($number, '.') + 1)); - $mantis = substr($value, (int)strpos($value, 'E') + 1); - if ($mantis < 0) { - $post += abs((int)$mantis); - } - - // TODO careless float could break financial math. - return number_format((float)$value, $post, '.', ''); - } - - // TODO careless float could break financial math. - return number_format((float)$value, 0, '.', ''); - } - public function opposite(?string $amount = null): ?string { if (null === $amount) { @@ -768,6 +725,33 @@ class Steam return $amount; } + private function convertAllBalances(array $others, TransactionCurrency $primary, Carbon $date): string + { + $total = '0'; + $converter = new ExchangeRateConverter(); + $singleton = PreferencesSingleton::getInstance(); + foreach ($others as $key => $amount) { + $preference = $singleton->getPreference($key); + + try { + $currency = $preference ?? Amount::getTransactionCurrencyByCode($key); + } catch (FireflyException) { + continue; + } + if (null === $preference) { + $singleton->setPreference($key, $currency); + } + $current = $amount; + if ($currency->id !== $primary->id) { + $current = $converter->convert($currency, $primary, $date, $amount); + Log::debug(sprintf('Convert %s %s to %s %s', $currency->code, $amount, $primary->code, $current)); + } + $total = bcadd($current, $total); + } + + return $total; + } + private function getCurrencies(Collection $accounts): array { $currencies = []; @@ -776,8 +760,8 @@ class Steam $primary = Amount::getPrimaryCurrency(); $currencies[$primary->id] = $primary; - $ids = $accounts->pluck('id')->toArray(); - $result = AccountMeta::whereIn('account_id', $ids)->where('name', 'currency_id')->get(); + $ids = $accounts->pluck('id')->toArray(); + $result = AccountMeta::whereIn('account_id', $ids)->where('name', 'currency_id')->get(); /** @var AccountMeta $item */ foreach ($result as $item) { @@ -787,7 +771,7 @@ class Steam } } // collect those currencies, skip primary because we already have it. - $set = TransactionCurrency::whereIn('id', $accountPreferences)->where('id', '!=', $primary->id)->get(); + $set = TransactionCurrency::whereIn('id', $accountPreferences)->where('id', '!=', $primary->id)->get(); foreach ($set as $item) { $currencies[$item->id] = $item; } @@ -798,7 +782,7 @@ class Steam $currencyPresent = isset($account->meta) && array_key_exists('currency', $account->meta) && null !== $account->meta['currency']; if ($currencyPresent) { $currencyId = $account->meta['currency']->id; - $currencies[$currencyId] ??= $account->meta['currency']; + $currencies[$currencyId] ??= $account->meta['currency']; $accountCurrencies[$accountId] = $account->meta['currency']; } if (!$currencyPresent && !array_key_exists($accountId, $accountPreferences)) { @@ -811,4 +795,16 @@ class Steam return $accountCurrencies; } + + private function groupAndSumTransactions(array $array, string $group, string $field): array + { + $return = []; + + foreach ($array as $item) { + $groupKey = $item[$group] ?? 'unknown'; + $return[$groupKey] = bcadd($return[$groupKey] ?? '0', (string)$item[$field]); + } + + return $return; + } } diff --git a/app/Support/System/GeneratesInstallationId.php b/app/Support/System/GeneratesInstallationId.php index 20cd0a303c..732237214f 100644 --- a/app/Support/System/GeneratesInstallationId.php +++ b/app/Support/System/GeneratesInstallationId.php @@ -49,7 +49,7 @@ trait GeneratesInstallationId if (null === $config) { $uuid4 = Uuid::uuid4(); - $uniqueId = (string) $uuid4; + $uniqueId = (string)$uuid4; app('log')->info(sprintf('Created Firefly III installation ID %s', $uniqueId)); app('fireflyconfig')->set('installation_id', $uniqueId); } diff --git a/app/Support/System/OAuthKeys.php b/app/Support/System/OAuthKeys.php index 53e353481f..1c1f2276cf 100644 --- a/app/Support/System/OAuthKeys.php +++ b/app/Support/System/OAuthKeys.php @@ -31,7 +31,6 @@ use Illuminate\Support\Facades\Crypt; use Laravel\Passport\Console\KeysCommand; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; - use function Safe\file_get_contents; use function Safe\file_put_contents; @@ -43,6 +42,78 @@ class OAuthKeys private const string PRIVATE_KEY = 'oauth_private_key'; private const string PUBLIC_KEY = 'oauth_public_key'; + public static function generateKeys(): void + { + Artisan::registerCommand(new KeysCommand()); + Artisan::call('firefly-iii:laravel-passport-keys'); + } + + public static function hasKeyFiles(): bool + { + $private = storage_path('oauth-private.key'); + $public = storage_path('oauth-public.key'); + + return file_exists($private) && file_exists($public); + } + + public static function keysInDatabase(): bool + { + $privateKey = ''; + $publicKey = ''; + // better check if keys are in the database: + if (app('fireflyconfig')->has(self::PRIVATE_KEY) && app('fireflyconfig')->has(self::PUBLIC_KEY)) { + try { + $privateKey = (string)app('fireflyconfig')->get(self::PRIVATE_KEY)?->data; + $publicKey = (string)app('fireflyconfig')->get(self::PUBLIC_KEY)?->data; + } catch (ContainerExceptionInterface | FireflyException | NotFoundExceptionInterface $e) { + app('log')->error(sprintf('Could not validate keysInDatabase(): %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); + } + } + if ('' !== $privateKey && '' !== $publicKey) { + return true; + } + + return false; + } + + /** + * @throws FireflyException + */ + public static function restoreKeysFromDB(): bool + { + $privateKey = (string)app('fireflyconfig')->get(self::PRIVATE_KEY)?->data; + $publicKey = (string)app('fireflyconfig')->get(self::PUBLIC_KEY)?->data; + + try { + $privateContent = Crypt::decrypt($privateKey); + $publicContent = Crypt::decrypt($publicKey); + } catch (DecryptException $e) { + app('log')->error('Could not decrypt pub/private keypair.'); + app('log')->error($e->getMessage()); + + // delete config vars from DB: + app('fireflyconfig')->delete(self::PRIVATE_KEY); + app('fireflyconfig')->delete(self::PUBLIC_KEY); + + return false; + } + $private = storage_path('oauth-private.key'); + $public = storage_path('oauth-public.key'); + file_put_contents($private, $privateContent); + file_put_contents($public, $publicContent); + + return true; + } + + public static function storeKeysInDB(): void + { + $private = storage_path('oauth-private.key'); + $public = storage_path('oauth-public.key'); + app('fireflyconfig')->set(self::PRIVATE_KEY, Crypt::encrypt(file_get_contents($private))); + app('fireflyconfig')->set(self::PUBLIC_KEY, Crypt::encrypt(file_get_contents($public))); + } + public static function verifyKeysRoutine(): void { if (!self::keysInDatabase() && !self::hasKeyFiles()) { @@ -60,76 +131,4 @@ class OAuthKeys self::storeKeysInDB(); } } - - public static function keysInDatabase(): bool - { - $privateKey = ''; - $publicKey = ''; - // better check if keys are in the database: - if (app('fireflyconfig')->has(self::PRIVATE_KEY) && app('fireflyconfig')->has(self::PUBLIC_KEY)) { - try { - $privateKey = (string) app('fireflyconfig')->get(self::PRIVATE_KEY)?->data; - $publicKey = (string) app('fireflyconfig')->get(self::PUBLIC_KEY)?->data; - } catch (ContainerExceptionInterface|FireflyException|NotFoundExceptionInterface $e) { - app('log')->error(sprintf('Could not validate keysInDatabase(): %s', $e->getMessage())); - app('log')->error($e->getTraceAsString()); - } - } - if ('' !== $privateKey && '' !== $publicKey) { - return true; - } - - return false; - } - - public static function hasKeyFiles(): bool - { - $private = storage_path('oauth-private.key'); - $public = storage_path('oauth-public.key'); - - return file_exists($private) && file_exists($public); - } - - public static function generateKeys(): void - { - Artisan::registerCommand(new KeysCommand()); - Artisan::call('firefly-iii:laravel-passport-keys'); - } - - public static function storeKeysInDB(): void - { - $private = storage_path('oauth-private.key'); - $public = storage_path('oauth-public.key'); - app('fireflyconfig')->set(self::PRIVATE_KEY, Crypt::encrypt(file_get_contents($private))); - app('fireflyconfig')->set(self::PUBLIC_KEY, Crypt::encrypt(file_get_contents($public))); - } - - /** - * @throws FireflyException - */ - public static function restoreKeysFromDB(): bool - { - $privateKey = (string) app('fireflyconfig')->get(self::PRIVATE_KEY)?->data; - $publicKey = (string) app('fireflyconfig')->get(self::PUBLIC_KEY)?->data; - - try { - $privateContent = Crypt::decrypt($privateKey); - $publicContent = Crypt::decrypt($publicKey); - } catch (DecryptException $e) { - app('log')->error('Could not decrypt pub/private keypair.'); - app('log')->error($e->getMessage()); - - // delete config vars from DB: - app('fireflyconfig')->delete(self::PRIVATE_KEY); - app('fireflyconfig')->delete(self::PUBLIC_KEY); - - return false; - } - $private = storage_path('oauth-private.key'); - $public = storage_path('oauth-public.key'); - file_put_contents($private, $privateContent); - file_put_contents($public, $publicContent); - - return true; - } } diff --git a/app/Support/Twig/AmountFormat.php b/app/Support/Twig/AmountFormat.php index 39cc1594db..49c9a6d11e 100644 --- a/app/Support/Twig/AmountFormat.php +++ b/app/Support/Twig/AmountFormat.php @@ -29,10 +29,10 @@ use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\Facades\Amount; use Illuminate\Support\Facades\Log; +use Override; use Twig\Extension\AbstractExtension; use Twig\TwigFilter; use Twig\TwigFunction; -use Override; /** * Contains all amount formatting routines. @@ -48,6 +48,17 @@ class AmountFormat extends AbstractExtension ]; } + #[Override] + public function getFunctions(): array + { + return [ + $this->formatAmountByAccount(), + $this->formatAmountBySymbol(), + $this->formatAmountByCurrency(), + $this->formatAmountByCode(), + ]; + } + protected function formatAmount(): TwigFilter { return new TwigFilter( @@ -61,30 +72,6 @@ class AmountFormat extends AbstractExtension ); } - protected function formatAmountPlain(): TwigFilter - { - return new TwigFilter( - 'formatAmountPlain', - static function (string $string): string { - $currency = Amount::getPrimaryCurrency(); - - return Amount::formatAnything($currency, $string, false); - }, - ['is_safe' => ['html']] - ); - } - - #[Override] - public function getFunctions(): array - { - return [ - $this->formatAmountByAccount(), - $this->formatAmountBySymbol(), - $this->formatAmountByCurrency(), - $this->formatAmountByCode(), - ]; - } - /** * Will format the amount by the currency related to the given account. * @@ -107,50 +94,6 @@ class AmountFormat extends AbstractExtension ); } - /** - * Will format the amount by the currency related to the given account. - */ - protected function formatAmountBySymbol(): TwigFunction - { - return new TwigFunction( - 'formatAmountBySymbol', - static function (string $amount, ?string $symbol = null, ?int $decimalPlaces = null, ?bool $coloured = null): string { - - if (null === $symbol) { - $message = sprintf('formatAmountBySymbol("%s", %s, %d, %s) was called without a symbol. Please browse to /flush to clear your cache.', $amount, var_export($symbol, true), $decimalPlaces, var_export($coloured, true)); - Log::error($message); - $currency = Amount::getPrimaryCurrency(); - } - if (null !== $symbol) { - $decimalPlaces ??= 2; - $coloured ??= true; - $currency = new TransactionCurrency(); - $currency->symbol = $symbol; - $currency->decimal_places = $decimalPlaces; - } - - return Amount::formatAnything($currency, $amount, $coloured); - }, - ['is_safe' => ['html']] - ); - } - - /** - * Will format the amount by the currency related to the given account. - */ - protected function formatAmountByCurrency(): TwigFunction - { - return new TwigFunction( - 'formatAmountByCurrency', - static function (TransactionCurrency $currency, string $amount, ?bool $coloured = null): string { - $coloured ??= true; - - return Amount::formatAnything($currency, $amount, $coloured); - }, - ['is_safe' => ['html']] - ); - } - /** * Use the code to format a currency. */ @@ -175,4 +118,61 @@ class AmountFormat extends AbstractExtension ['is_safe' => ['html']] ); } + + /** + * Will format the amount by the currency related to the given account. + */ + protected function formatAmountByCurrency(): TwigFunction + { + return new TwigFunction( + 'formatAmountByCurrency', + static function (TransactionCurrency $currency, string $amount, ?bool $coloured = null): string { + $coloured ??= true; + + return Amount::formatAnything($currency, $amount, $coloured); + }, + ['is_safe' => ['html']] + ); + } + + /** + * Will format the amount by the currency related to the given account. + */ + protected function formatAmountBySymbol(): TwigFunction + { + return new TwigFunction( + 'formatAmountBySymbol', + static function (string $amount, ?string $symbol = null, ?int $decimalPlaces = null, ?bool $coloured = null): string { + + if (null === $symbol) { + $message = sprintf('formatAmountBySymbol("%s", %s, %d, %s) was called without a symbol. Please browse to /flush to clear your cache.', $amount, var_export($symbol, true), $decimalPlaces, var_export($coloured, true)); + Log::error($message); + $currency = Amount::getPrimaryCurrency(); + } + if (null !== $symbol) { + $decimalPlaces ??= 2; + $coloured ??= true; + $currency = new TransactionCurrency(); + $currency->symbol = $symbol; + $currency->decimal_places = $decimalPlaces; + } + + return Amount::formatAnything($currency, $amount, $coloured); + }, + ['is_safe' => ['html']] + ); + } + + protected function formatAmountPlain(): TwigFilter + { + return new TwigFilter( + 'formatAmountPlain', + static function (string $string): string { + $currency = Amount::getPrimaryCurrency(); + + return Amount::formatAnything($currency, $string, false); + }, + ['is_safe' => ['html']] + ); + } } diff --git a/app/Support/Twig/General.php b/app/Support/Twig/General.php index 6f71d6f578..337e832312 100644 --- a/app/Support/Twig/General.php +++ b/app/Support/Twig/General.php @@ -37,7 +37,6 @@ use Override; use Twig\Extension\AbstractExtension; use Twig\TwigFilter; use Twig\TwigFunction; - use function Safe\parse_url; /** @@ -57,144 +56,6 @@ class General extends AbstractExtension ]; } - /** - * Show account balance. Only used on the front page of Firefly III. - */ - protected function balance(): TwigFilter - { - return new TwigFilter( - 'balance', - static function (?Account $account): string { - if (!$account instanceof Account) { - return '0'; - } - - /** @var Carbon $date */ - $date = session('end', today(config('app.timezone'))->endOfMonth()); - Log::debug(sprintf('twig balance: Call finalAccountBalance with date/time "%s"', $date->toIso8601String())); - $info = Steam::finalAccountBalance($account, $date); - $currency = Steam::getAccountCurrency($account); - $primary = Amount::getPrimaryCurrency(); - $convertToPrimary = Amount::convertToPrimary(); - $usePrimary = $convertToPrimary && $primary->id !== $currency->id; - $currency ??= $primary; - $strings = []; - foreach ($info as $key => $balance) { - if ('balance' === $key) { - // balance in account currency. - if (!$usePrimary) { - $strings[] = app('amount')->formatAnything($currency, $balance, false); - } - - continue; - } - if ('pc_balance' === $key) { - // balance in primary currency. - if ($usePrimary) { - $strings[] = app('amount')->formatAnything($primary, $balance, false); - } - - continue; - } - // for multi currency accounts. - if ($usePrimary && $key !== $primary->code) { - $strings[] = app('amount')->formatAnything(Amount::getTransactionCurrencyByCode($key), $balance, false); - } - } - - return implode(', ', $strings); - // return app('steam')->balance($account, $date); - } - ); - } - - /** - * Used to convert 1024 to 1kb etc. - */ - protected function formatFilesize(): TwigFilter - { - return new TwigFilter( - 'filesize', - static function (int $size): string { - // less than one GB, more than one MB - if ($size < (1024 * 1024 * 2014) && $size >= (1024 * 1024)) { - return round($size / (1024 * 1024), 2).' MB'; - } - - // less than one MB - if ($size < (1024 * 1024)) { - return round($size / 1024, 2).' KB'; - } - - return $size.' bytes'; - } - ); - } - - /** - * Show icon with attachment. - * - * @SuppressWarnings("PHPMD.CyclomaticComplexity") - */ - protected function mimeIcon(): TwigFilter - { - return new TwigFilter( - 'mimeIcon', - static fn (string $string): string => match ($string) { - 'application/pdf' => 'fa-file-pdf-o', - 'image/webp', 'image/png', 'image/jpeg', 'image/svg+xml', 'image/heic', 'image/heic-sequence', 'application/vnd.oasis.opendocument.image' => 'fa-file-image-o', - 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 'application/x-iwork-pages-sffpages', 'application/vnd.sun.xml.writer', 'application/vnd.sun.xml.writer.template', 'application/vnd.sun.xml.writer.global', 'application/vnd.stardivision.writer', 'application/vnd.stardivision.writer-global', 'application/vnd.oasis.opendocument.text', 'application/vnd.oasis.opendocument.text-template', 'application/vnd.oasis.opendocument.text-web', 'application/vnd.oasis.opendocument.text-master' => 'fa-file-word-o', - 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', 'application/vnd.sun.xml.calc', 'application/vnd.sun.xml.calc.template', 'application/vnd.stardivision.calc', 'application/vnd.oasis.opendocument.spreadsheet', 'application/vnd.oasis.opendocument.spreadsheet-template' => 'fa-file-excel-o', - 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/vnd.openxmlformats-officedocument.presentationml.template', 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', 'application/vnd.sun.xml.impress', 'application/vnd.sun.xml.impress.template', 'application/vnd.stardivision.impress', 'application/vnd.oasis.opendocument.presentation', 'application/vnd.oasis.opendocument.presentation-template' => 'fa-file-powerpoint-o', - 'application/vnd.sun.xml.draw', 'application/vnd.sun.xml.draw.template', 'application/vnd.stardivision.draw', 'application/vnd.oasis.opendocument.chart' => 'fa-paint-brush', - 'application/vnd.oasis.opendocument.graphics', 'application/vnd.oasis.opendocument.graphics-template', 'application/vnd.sun.xml.math', 'application/vnd.stardivision.math', 'application/vnd.oasis.opendocument.formula', 'application/vnd.oasis.opendocument.database' => 'fa-calculator', - default => 'fa-file-o', - }, - ['is_safe' => ['html']] - ); - } - - protected function markdown(): TwigFilter - { - return new TwigFilter( - 'markdown', - static function (string $text): string { - $converter = new GithubFlavoredMarkdownConverter( - [ - 'allow_unsafe_links' => false, - 'max_nesting_level' => 5, - 'html_input' => 'escape', - ] - ); - - return (string)$converter->convert($text); - }, - ['is_safe' => ['html']] - ); - } - - /** - * Show URL host name - */ - protected function phpHostName(): TwigFilter - { - return new TwigFilter( - 'phphost', - static function (string $string): string { - $proto = parse_url($string, PHP_URL_SCHEME); - $host = parse_url($string, PHP_URL_HOST); - if (is_array($host)) { - $host = implode(' ', $host); - } - if (is_array($proto)) { - $proto = implode(' ', $proto); - } - - return e(sprintf('%s://%s', $proto, $host)); - } - ); - } - #[Override] public function getFunctions(): array { @@ -212,38 +73,6 @@ class General extends AbstractExtension ]; } - /** - * Basic example thing for some views. - */ - protected function phpdate(): TwigFunction - { - return new TwigFunction( - 'phpdate', - static fn (string $str): string => date($str) - ); - } - - /** - * Will return "active" when the current route matches the given argument - * exactly. - */ - protected function activeRouteStrict(): TwigFunction - { - return new TwigFunction( - 'activeRouteStrict', - static function (): string { - $args = func_get_args(); - $route = $args[0]; // name of the route. - - if (\Route::getCurrentRoute()->getName() === $route) { - return 'active'; - } - - return ''; - } - ); - } - /** * Will return "active" when a part of the route matches the argument. * ie. "accounts" will match "accounts.index". @@ -275,7 +104,7 @@ class General extends AbstractExtension 'activeRoutePartialObjectType', static function ($context): string { [, $route, $objectType] = func_get_args(); - $activeObjectType = $context['objectType'] ?? false; + $activeObjectType = $context['objectType'] ?? false; if ($objectType === $activeObjectType && false !== stripos( @@ -291,6 +120,193 @@ class General extends AbstractExtension ); } + /** + * Will return "active" when the current route matches the given argument + * exactly. + */ + protected function activeRouteStrict(): TwigFunction + { + return new TwigFunction( + 'activeRouteStrict', + static function (): string { + $args = func_get_args(); + $route = $args[0]; // name of the route. + + if (\Route::getCurrentRoute()->getName() === $route) { + return 'active'; + } + + return ''; + } + ); + } + + /** + * Show account balance. Only used on the front page of Firefly III. + */ + protected function balance(): TwigFilter + { + return new TwigFilter( + 'balance', + static function (?Account $account): string { + if (!$account instanceof Account) { + return '0'; + } + + /** @var Carbon $date */ + $date = session('end', today(config('app.timezone'))->endOfMonth()); + Log::debug(sprintf('twig balance: Call finalAccountBalance with date/time "%s"', $date->toIso8601String())); + $info = Steam::finalAccountBalance($account, $date); + $currency = Steam::getAccountCurrency($account); + $primary = Amount::getPrimaryCurrency(); + $convertToPrimary = Amount::convertToPrimary(); + $usePrimary = $convertToPrimary && $primary->id !== $currency->id; + $currency ??= $primary; + $strings = []; + foreach ($info as $key => $balance) { + if ('balance' === $key) { + // balance in account currency. + if (!$usePrimary) { + $strings[] = app('amount')->formatAnything($currency, $balance, false); + } + + continue; + } + if ('pc_balance' === $key) { + // balance in primary currency. + if ($usePrimary) { + $strings[] = app('amount')->formatAnything($primary, $balance, false); + } + + continue; + } + // for multi currency accounts. + if ($usePrimary && $key !== $primary->code) { + $strings[] = app('amount')->formatAnything(Amount::getTransactionCurrencyByCode($key), $balance, false); + } + } + + return implode(', ', $strings); + // return app('steam')->balance($account, $date); + } + ); + } + + protected function carbonize(): TwigFunction + { + return new TwigFunction( + 'carbonize', + static fn(string $date): Carbon => new Carbon($date, config('app.timezone')) + ); + } + + /** + * Formats a string as a thing by converting it to a Carbon first. + */ + protected function formatDate(): TwigFunction + { + return new TwigFunction( + 'formatDate', + static function (string $date, string $format): string { + $carbon = new Carbon($date); + + return $carbon->isoFormat($format); + } + ); + } + + /** + * Used to convert 1024 to 1kb etc. + */ + protected function formatFilesize(): TwigFilter + { + return new TwigFilter( + 'filesize', + static function (int $size): string { + // less than one GB, more than one MB + if ($size < (1024 * 1024 * 2014) && $size >= (1024 * 1024)) { + return round($size / (1024 * 1024), 2) . ' MB'; + } + + // less than one MB + if ($size < (1024 * 1024)) { + return round($size / 1024, 2) . ' KB'; + } + + return $size . ' bytes'; + } + ); + } + + /** + * TODO Remove me when v2 hits. + */ + protected function getMetaField(): TwigFunction + { + return new TwigFunction( + 'accountGetMetaField', + static function (Account $account, string $field): string { + /** @var AccountRepositoryInterface $repository */ + $repository = app(AccountRepositoryInterface::class); + $result = $repository->getMetaValue($account, $field); + if (null === $result) { + return ''; + } + + return $result; + } + ); + } + + protected function getRootSearchOperator(): TwigFunction + { + return new TwigFunction( + 'getRootSearchOperator', + static function (string $operator): string { + $result = OperatorQuerySearch::getRootOperator($operator); + + return str_replace('-', 'not_', $result); + } + ); + } + + /** + * Will return true if the user is of role X. + */ + protected function hasRole(): TwigFunction + { + return new TwigFunction( + 'hasRole', + static function (string $role): bool { + $repository = app(UserRepositoryInterface::class); + if ($repository->hasRole(auth()->user(), $role)) { + return true; + } + + return false; + } + ); + } + + protected function markdown(): TwigFilter + { + return new TwigFilter( + 'markdown', + static function (string $text): string { + $converter = new GithubFlavoredMarkdownConverter( + [ + 'allow_unsafe_links' => false, + 'max_nesting_level' => 5, + 'html_input' => 'escape', + ] + ); + + return (string)$converter->convert($text); + }, + ['is_safe' => ['html']] + ); + } + /** * Will return "menu-open" when a part of the route matches the argument. * ie. "accounts" will match "accounts.index". @@ -313,75 +329,58 @@ class General extends AbstractExtension } /** - * Formats a string as a thing by converting it to a Carbon first. + * Show icon with attachment. + * + * @SuppressWarnings("PHPMD.CyclomaticComplexity") */ - protected function formatDate(): TwigFunction + protected function mimeIcon(): TwigFilter { - return new TwigFunction( - 'formatDate', - static function (string $date, string $format): string { - $carbon = new Carbon($date); + return new TwigFilter( + 'mimeIcon', + static fn(string $string): string => match ($string) { + 'application/pdf' => 'fa-file-pdf-o', + 'image/webp', 'image/png', 'image/jpeg', 'image/svg+xml', 'image/heic', 'image/heic-sequence', 'application/vnd.oasis.opendocument.image' => 'fa-file-image-o', + 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 'application/x-iwork-pages-sffpages', 'application/vnd.sun.xml.writer', 'application/vnd.sun.xml.writer.template', 'application/vnd.sun.xml.writer.global', 'application/vnd.stardivision.writer', 'application/vnd.stardivision.writer-global', 'application/vnd.oasis.opendocument.text', 'application/vnd.oasis.opendocument.text-template', 'application/vnd.oasis.opendocument.text-web', 'application/vnd.oasis.opendocument.text-master' => 'fa-file-word-o', + 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', 'application/vnd.sun.xml.calc', 'application/vnd.sun.xml.calc.template', 'application/vnd.stardivision.calc', 'application/vnd.oasis.opendocument.spreadsheet', 'application/vnd.oasis.opendocument.spreadsheet-template' => 'fa-file-excel-o', + 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/vnd.openxmlformats-officedocument.presentationml.template', 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', 'application/vnd.sun.xml.impress', 'application/vnd.sun.xml.impress.template', 'application/vnd.stardivision.impress', 'application/vnd.oasis.opendocument.presentation', 'application/vnd.oasis.opendocument.presentation-template' => 'fa-file-powerpoint-o', + 'application/vnd.sun.xml.draw', 'application/vnd.sun.xml.draw.template', 'application/vnd.stardivision.draw', 'application/vnd.oasis.opendocument.chart' => 'fa-paint-brush', + 'application/vnd.oasis.opendocument.graphics', 'application/vnd.oasis.opendocument.graphics-template', 'application/vnd.sun.xml.math', 'application/vnd.stardivision.math', 'application/vnd.oasis.opendocument.formula', 'application/vnd.oasis.opendocument.database' => 'fa-calculator', + default => 'fa-file-o', + }, + ['is_safe' => ['html']] + ); + } - return $carbon->isoFormat($format); + /** + * Show URL host name + */ + protected function phpHostName(): TwigFilter + { + return new TwigFilter( + 'phphost', + static function (string $string): string { + $proto = parse_url($string, PHP_URL_SCHEME); + $host = parse_url($string, PHP_URL_HOST); + if (is_array($host)) { + $host = implode(' ', $host); + } + if (is_array($proto)) { + $proto = implode(' ', $proto); + } + + return e(sprintf('%s://%s', $proto, $host)); } ); } /** - * TODO Remove me when v2 hits. + * Basic example thing for some views. */ - protected function getMetaField(): TwigFunction + protected function phpdate(): TwigFunction { return new TwigFunction( - 'accountGetMetaField', - static function (Account $account, string $field): string { - /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class); - $result = $repository->getMetaValue($account, $field); - if (null === $result) { - return ''; - } - - return $result; - } - ); - } - - /** - * Will return true if the user is of role X. - */ - protected function hasRole(): TwigFunction - { - return new TwigFunction( - 'hasRole', - static function (string $role): bool { - $repository = app(UserRepositoryInterface::class); - if ($repository->hasRole(auth()->user(), $role)) { - return true; - } - - return false; - } - ); - } - - protected function getRootSearchOperator(): TwigFunction - { - return new TwigFunction( - 'getRootSearchOperator', - static function (string $operator): string { - $result = OperatorQuerySearch::getRootOperator($operator); - - return str_replace('-', 'not_', $result); - } - ); - } - - protected function carbonize(): TwigFunction - { - return new TwigFunction( - 'carbonize', - static fn (string $date): Carbon => new Carbon($date, config('app.timezone')) + 'phpdate', + static fn(string $str): string => date($str) ); } } diff --git a/app/Support/Twig/Rule.php b/app/Support/Twig/Rule.php index c833745d40..7ed872df8b 100644 --- a/app/Support/Twig/Rule.php +++ b/app/Support/Twig/Rule.php @@ -23,34 +23,43 @@ declare(strict_types=1); namespace FireflyIII\Support\Twig; -use Twig\Extension\AbstractExtension; -use Twig\TwigFunction; use Config; use Override; +use Twig\Extension\AbstractExtension; +use Twig\TwigFunction; /** * Class Rule. */ class Rule extends AbstractExtension { - #[Override] - public function getFunctions(): array + public function allActionTriggers(): TwigFunction { - return [ - $this->allJournalTriggers(), - $this->allRuleTriggers(), - $this->allActionTriggers(), - ]; + return new TwigFunction( + 'allRuleActions', + static function () { + // array of valid values for actions + $ruleActions = array_keys(Config::get('firefly.rule-actions')); + $possibleActions = []; + foreach ($ruleActions as $key) { + $possibleActions[$key] = (string)trans('firefly.rule_action_' . $key . '_choice'); + } + unset($ruleActions); + asort($possibleActions); + + return $possibleActions; + } + ); } public function allJournalTriggers(): TwigFunction { return new TwigFunction( 'allJournalTriggers', - static fn () => [ - 'store-journal' => (string) trans('firefly.rule_trigger_store_journal'), - 'update-journal' => (string) trans('firefly.rule_trigger_update_journal'), - 'manual-activation' => (string) trans('firefly.rule_trigger_manual'), + static fn() => [ + 'store-journal' => (string)trans('firefly.rule_trigger_store_journal'), + 'update-journal' => (string)trans('firefly.rule_trigger_update_journal'), + 'manual-activation' => (string)trans('firefly.rule_trigger_manual'), ] ); } @@ -64,7 +73,7 @@ class Rule extends AbstractExtension $possibleTriggers = []; foreach ($ruleTriggers as $key) { if ('user_action' !== $key) { - $possibleTriggers[$key] = (string) trans('firefly.rule_trigger_'.$key.'_choice'); + $possibleTriggers[$key] = (string)trans('firefly.rule_trigger_' . $key . '_choice'); } } unset($ruleTriggers); @@ -75,22 +84,13 @@ class Rule extends AbstractExtension ); } - public function allActionTriggers(): TwigFunction + #[Override] + public function getFunctions(): array { - return new TwigFunction( - 'allRuleActions', - static function () { - // array of valid values for actions - $ruleActions = array_keys(Config::get('firefly.rule-actions')); - $possibleActions = []; - foreach ($ruleActions as $key) { - $possibleActions[$key] = (string) trans('firefly.rule_action_'.$key.'_choice'); - } - unset($ruleActions); - asort($possibleActions); - - return $possibleActions; - } - ); + return [ + $this->allJournalTriggers(), + $this->allRuleTriggers(), + $this->allActionTriggers(), + ]; } } diff --git a/app/Support/Twig/TransactionGroupTwig.php b/app/Support/Twig/TransactionGroupTwig.php index 81cf8db231..e2957ec3a1 100644 --- a/app/Support/Twig/TransactionGroupTwig.php +++ b/app/Support/Twig/TransactionGroupTwig.php @@ -31,10 +31,9 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournalMeta; use Illuminate\Support\Facades\DB; +use Override; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; -use Override; - use function Safe\json_decode; /** @@ -76,6 +75,141 @@ class TransactionGroupTwig extends AbstractExtension ); } + public function journalGetMetaDate(): TwigFunction + { + return new TwigFunction( + 'journalGetMetaDate', + static function (int $journalId, string $metaField) { + /** @var null|TransactionJournalMeta $entry */ + $entry = DB::table('journal_meta') + ->where('name', $metaField) + ->where('transaction_journal_id', $journalId) + ->whereNull('deleted_at') + ->first(); + if (null === $entry) { + return today(config('app.timezone')); + } + + return new Carbon(json_decode((string)$entry->data, false)); + } + ); + } + + public function journalGetMetaField(): TwigFunction + { + return new TwigFunction( + 'journalGetMetaField', + static function (int $journalId, string $metaField) { + /** @var null|TransactionJournalMeta $entry */ + $entry = DB::table('journal_meta') + ->where('name', $metaField) + ->where('transaction_journal_id', $journalId) + ->whereNull('deleted_at') + ->first(); + if (null === $entry) { + return ''; + } + + return json_decode((string)$entry->data, true); + } + ); + } + + public function journalHasMeta(): TwigFunction + { + return new TwigFunction( + 'journalHasMeta', + static function (int $journalId, string $metaField) { + $count = DB::table('journal_meta') + ->where('name', $metaField) + ->where('transaction_journal_id', $journalId) + ->whereNull('deleted_at') + ->count(); + + return 1 === $count; + } + ); + } + + /** + * Shows the amount for a single journal object. + */ + public function journalObjectAmount(): TwigFunction + { + return new TwigFunction( + 'journalObjectAmount', + function (TransactionJournal $journal): string { + $result = $this->normalJournalObjectAmount($journal); + // now append foreign amount, if any. + if ($this->journalObjectHasForeign($journal)) { + $foreign = $this->foreignJournalObjectAmount($journal); + $result = sprintf('%s (%s)', $result, $foreign); + } + + return $result; + }, + ['is_safe' => ['html']] + ); + } + + /** + * Generate foreign amount for transaction from a transaction group. + */ + private function foreignJournalArrayAmount(array $array): string + { + $type = $array['transaction_type_type'] ?? TransactionTypeEnum::WITHDRAWAL->value; + $amount = $array['foreign_amount'] ?? '0'; + $colored = true; + + $sourceType = $array['source_account_type'] ?? 'invalid'; + $amount = $this->signAmount($amount, $type, $sourceType); + + if (TransactionTypeEnum::TRANSFER->value === $type) { + $colored = false; + } + $result = app('amount')->formatFlat($array['foreign_currency_symbol'], (int)$array['foreign_currency_decimal_places'], $amount, $colored); + if (TransactionTypeEnum::TRANSFER->value === $type) { + return sprintf('%s', $result); + } + + return $result; + } + + /** + * Generate foreign amount for journal from a transaction group. + */ + private function foreignJournalObjectAmount(TransactionJournal $journal): string + { + $type = $journal->transactionType->type; + + /** @var Transaction $first */ + $first = $journal->transactions()->where('amount', '<', 0)->first(); + $currency = $first->foreignCurrency; + $amount = '' === $first->foreign_amount ? '0' : $first->foreign_amount; + $colored = true; + $sourceType = $first->account->accountType()->first()->type; + + $amount = $this->signAmount($amount, $type, $sourceType); + + if (TransactionTypeEnum::TRANSFER->value === $type) { + $colored = false; + } + $result = app('amount')->formatFlat($currency->symbol, $currency->decimal_places, $amount, $colored); + if (TransactionTypeEnum::TRANSFER->value === $type) { + return sprintf('%s', $result); + } + + return $result; + } + + private function journalObjectHasForeign(TransactionJournal $journal): bool + { + /** @var Transaction $first */ + $first = $journal->transactions()->where('amount', '<', 0)->first(); + + return '' !== $first->foreign_amount; + } + /** * Generate normal amount for transaction from a transaction group. */ @@ -91,7 +225,34 @@ class TransactionGroupTwig extends AbstractExtension $colored = false; } - $result = app('amount')->formatFlat($array['currency_symbol'], (int) $array['currency_decimal_places'], $amount, $colored); + $result = app('amount')->formatFlat($array['currency_symbol'], (int)$array['currency_decimal_places'], $amount, $colored); + if (TransactionTypeEnum::TRANSFER->value === $type) { + return sprintf('%s', $result); + } + + return $result; + } + + /** + * Generate normal amount for transaction from a transaction group. + */ + private function normalJournalObjectAmount(TransactionJournal $journal): string + { + $type = $journal->transactionType->type; + + /** @var Transaction $first */ + $first = $journal->transactions()->where('amount', '<', 0)->first(); + $currency = $journal->transactionCurrency; + $amount = $first->amount ?? '0'; + $colored = true; + $sourceType = $first->account->accountType()->first()->type; + + $amount = $this->signAmount($amount, $type, $sourceType); + + if (TransactionTypeEnum::TRANSFER->value === $type) { + $colored = false; + } + $result = app('amount')->formatFlat($currency->symbol, $currency->decimal_places, $amount, $colored); if (TransactionTypeEnum::TRANSFER->value === $type) { return sprintf('%s', $result); } @@ -118,169 +279,4 @@ class TransactionGroupTwig extends AbstractExtension return $amount; } - - /** - * Generate foreign amount for transaction from a transaction group. - */ - private function foreignJournalArrayAmount(array $array): string - { - $type = $array['transaction_type_type'] ?? TransactionTypeEnum::WITHDRAWAL->value; - $amount = $array['foreign_amount'] ?? '0'; - $colored = true; - - $sourceType = $array['source_account_type'] ?? 'invalid'; - $amount = $this->signAmount($amount, $type, $sourceType); - - if (TransactionTypeEnum::TRANSFER->value === $type) { - $colored = false; - } - $result = app('amount')->formatFlat($array['foreign_currency_symbol'], (int) $array['foreign_currency_decimal_places'], $amount, $colored); - if (TransactionTypeEnum::TRANSFER->value === $type) { - return sprintf('%s', $result); - } - - return $result; - } - - /** - * Shows the amount for a single journal object. - */ - public function journalObjectAmount(): TwigFunction - { - return new TwigFunction( - 'journalObjectAmount', - function (TransactionJournal $journal): string { - $result = $this->normalJournalObjectAmount($journal); - // now append foreign amount, if any. - if ($this->journalObjectHasForeign($journal)) { - $foreign = $this->foreignJournalObjectAmount($journal); - $result = sprintf('%s (%s)', $result, $foreign); - } - - return $result; - }, - ['is_safe' => ['html']] - ); - } - - /** - * Generate normal amount for transaction from a transaction group. - */ - private function normalJournalObjectAmount(TransactionJournal $journal): string - { - $type = $journal->transactionType->type; - - /** @var Transaction $first */ - $first = $journal->transactions()->where('amount', '<', 0)->first(); - $currency = $journal->transactionCurrency; - $amount = $first->amount ?? '0'; - $colored = true; - $sourceType = $first->account->accountType()->first()->type; - - $amount = $this->signAmount($amount, $type, $sourceType); - - if (TransactionTypeEnum::TRANSFER->value === $type) { - $colored = false; - } - $result = app('amount')->formatFlat($currency->symbol, $currency->decimal_places, $amount, $colored); - if (TransactionTypeEnum::TRANSFER->value === $type) { - return sprintf('%s', $result); - } - - return $result; - } - - private function journalObjectHasForeign(TransactionJournal $journal): bool - { - /** @var Transaction $first */ - $first = $journal->transactions()->where('amount', '<', 0)->first(); - - return '' !== $first->foreign_amount; - } - - /** - * Generate foreign amount for journal from a transaction group. - */ - private function foreignJournalObjectAmount(TransactionJournal $journal): string - { - $type = $journal->transactionType->type; - - /** @var Transaction $first */ - $first = $journal->transactions()->where('amount', '<', 0)->first(); - $currency = $first->foreignCurrency; - $amount = '' === $first->foreign_amount ? '0' : $first->foreign_amount; - $colored = true; - $sourceType = $first->account->accountType()->first()->type; - - $amount = $this->signAmount($amount, $type, $sourceType); - - if (TransactionTypeEnum::TRANSFER->value === $type) { - $colored = false; - } - $result = app('amount')->formatFlat($currency->symbol, $currency->decimal_places, $amount, $colored); - if (TransactionTypeEnum::TRANSFER->value === $type) { - return sprintf('%s', $result); - } - - return $result; - } - - public function journalHasMeta(): TwigFunction - { - return new TwigFunction( - 'journalHasMeta', - static function (int $journalId, string $metaField) { - $count = DB::table('journal_meta') - ->where('name', $metaField) - ->where('transaction_journal_id', $journalId) - ->whereNull('deleted_at') - ->count() - ; - - return 1 === $count; - } - ); - } - - public function journalGetMetaDate(): TwigFunction - { - return new TwigFunction( - 'journalGetMetaDate', - static function (int $journalId, string $metaField) { - /** @var null|TransactionJournalMeta $entry */ - $entry = DB::table('journal_meta') - ->where('name', $metaField) - ->where('transaction_journal_id', $journalId) - ->whereNull('deleted_at') - ->first() - ; - if (null === $entry) { - return today(config('app.timezone')); - } - - return new Carbon(json_decode((string) $entry->data, false)); - } - ); - } - - public function journalGetMetaField(): TwigFunction - { - return new TwigFunction( - 'journalGetMetaField', - static function (int $journalId, string $metaField) { - /** @var null|TransactionJournalMeta $entry */ - $entry = DB::table('journal_meta') - ->where('name', $metaField) - ->where('transaction_journal_id', $journalId) - ->whereNull('deleted_at') - ->first() - ; - if (null === $entry) { - return ''; - } - - return json_decode((string) $entry->data, true); - } - ); - } } diff --git a/app/Support/Twig/Translation.php b/app/Support/Twig/Translation.php index bb19890ff9..e4f429f07e 100644 --- a/app/Support/Twig/Translation.php +++ b/app/Support/Twig/Translation.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Support\Twig; +use Override; use Twig\Extension\AbstractExtension; use Twig\TwigFilter; use Twig\TwigFunction; -use Override; /** * Class Budget. @@ -39,7 +39,7 @@ class Translation extends AbstractExtension return [ new TwigFilter( '_', - static fn ($name) => (string) trans(sprintf('firefly.%s', $name)), + static fn($name) => (string)trans(sprintf('firefly.%s', $name)), ['is_safe' => ['html']] ), ]; From 69dfbda847f9d141bfe4bea391b99ea1728af090 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 26 Sep 2025 06:06:43 +0200 Subject: [PATCH 41/91] Add empty statistic if necessary. --- app/Support/Http/Controllers/PeriodOverview.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index 1dda34bf8d..1914b7ce0b 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -550,6 +550,9 @@ trait PeriodOverview foreach ($array as $entry) { $this->periodStatisticRepo->saveStatistic($account, $entry['currency_id'], $start, $end, $type, $entry['count'], $entry['amount']); } + if(0 === count($array)) { + $this->periodStatisticRepo->saveStatistic($account, $this->primaryCurrency->id, $start, $end, $type, 0, '0'); + } } /** From 18ae950d2ef861a8aef9463bfa59d867729cc0b6 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 26 Sep 2025 06:09:44 +0200 Subject: [PATCH 42/91] Optimize queries. --- app/Support/Steam.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/Support/Steam.php b/app/Support/Steam.php index c9a13be8b7..32b7c160bd 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -590,6 +590,11 @@ class Steam */ public function getLocale(): string // get preference { + $singleton = PreferencesSingleton::getInstance(); + $cached = $singleton->getPreference('locale'); + if(null !== $cached) { + return $cached; + } $locale = app('preferences')->get('locale', config('firefly.default_locale', 'equal'))->data; if (is_array($locale)) { $locale = 'equal'; @@ -601,9 +606,9 @@ class Steam // Check for Windows to replace the locale correctly. if ('WIN' === strtoupper(substr(PHP_OS, 0, 3))) { - return str_replace('_', '-', $locale); + $locale = str_replace('_', '-', $locale); } - + $singleton->setPreference('locale', $locale); return $locale; } From 8b09cfb8c97ee5d25dfc7f1dc39df51bef1c6b74 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 26 Sep 2025 19:32:53 +0200 Subject: [PATCH 43/91] Optimize query for period statistics. --- .../Account/AccountRepository.php | 1 + .../PeriodStatisticRepository.php | 27 ++++++++------- .../Http/Controllers/PeriodOverview.php | 34 +++++++++++++++---- 3 files changed, 44 insertions(+), 18 deletions(-) diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index c2a14d8d81..caf928b771 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -546,6 +546,7 @@ class AccountRepository implements AccountRepositoryInterface, UserGroupInterfac #[Override] public function periodCollection(Account $account, Carbon $start, Carbon $end): array { + Log::debug(sprintf('periodCollection(#%d, %s, %s)', $account->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); return $account->transactions() ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') diff --git a/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php index 1762329f12..00da6827ee 100644 --- a/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php +++ b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php @@ -27,32 +27,31 @@ use Carbon\Carbon; use FireflyIII\Models\PeriodStatistic; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Log; class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface { public function findPeriodStatistics(Model $model, Carbon $start, Carbon $end, array $types): Collection { return $model->primaryPeriodStatistics() - ->where('start', $start) - ->where('end', $end) - ->whereIn('type', $types) - ->get() - ; + ->where('start', $start) + ->where('end', $end) + ->whereIn('type', $types) + ->get(); } public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection { return $model->primaryPeriodStatistics() - ->where('start', $start) - ->where('end', $end) - ->where('type', $type) - ->get() - ; + ->where('start', $start) + ->where('end', $end) + ->where('type', $type) + ->get(); } public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic { - $stat = new PeriodStatistic(); + $stat = new PeriodStatistic(); $stat->primaryStatable()->associate($model); $stat->transaction_currency_id = $currencyId; $stat->start = $start; @@ -64,11 +63,15 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface $stat->type = $type; $stat->save(); + Log::debug(sprintf('Saved #%d [currency #%d, Model %s #%d, %s to %s, %d, %s] as new statistic.', + $stat->id, get_class($model), $model->id, $stat->transaction_currency_id, $stat->start->toW3cString(), $stat->end->toW3cString(), $count, $amount + )); + return $stat; } public function allInRangeForModel(Model $model, Carbon $start, Carbon $end): Collection { - return $model->primaryPeriodStatistics()->where('start','>=', $start)->where('end','<=', $end)->get(); + return $model->primaryPeriodStatistics()->where('start', '>=', $start)->where('end', '<=', $end)->get(); } } diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index 1914b7ce0b..9012e471b9 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -90,6 +90,9 @@ trait PeriodOverview $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + /** @var array $dates */ + $dates = Navigation::blockPeriods($start, $end, $range); + [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); $this->statistics = $this->periodStatisticRepo->allInRangeForModel($account, $start, $end); // TODO needs to be re-arranged: @@ -97,10 +100,8 @@ trait PeriodOverview // loop blocks, an loop the types, and select the missing ones. // create new ones, or use collected. - /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; - $types = ['spent', 'earned', 'transferred_in', 'transferred_away']; Log::debug(sprintf('Count of loops: %d', count($dates))); foreach ($dates as $currentDate) { $entries[] = $this->getSingleAccountPeriod($account, $currentDate['period'], $currentDate['start'], $currentDate['end']); @@ -110,6 +111,25 @@ trait PeriodOverview return $entries; } + private function getPeriodFromBlocks(array $dates, Carbon $start, Carbon $end): array + { + Log::debug('Filter generated periods to select the oldest and newest date.'); + foreach ($dates as $row) { + $currentStart = clone $row['start']; + $currentEnd = clone $row['end']; + if ($currentStart->lt($start)) { + Log::debug(sprintf('New start: was %s, now %s', $start->format('Y-m-d'), $currentStart->format('Y-m-d'))); + $start = $currentStart; + } + if ($currentEnd->gt($end)) { + Log::debug(sprintf('New end: was %s, now %s', $end->format('Y-m-d'), $currentEnd->format('Y-m-d'))); + $end = $currentEnd; + } + } + + return [$start, $end]; + } + /** * Overview for single category. Has been refactored recently. * @@ -326,7 +346,7 @@ trait PeriodOverview { return $this->statistics->filter( function (PeriodStatistic $statistic) use ($start, $end, $type) { - if( + if ( !$statistic->end->equalTo($end) && $statistic->end->format('Y-m-d H:i:s') === $end->format('Y-m-d H:i:s') ) { @@ -377,9 +397,9 @@ trait PeriodOverview break; } // each result must be grouped by currency, then saved as period statistic. + Log::debug(sprintf('Going to group %d found journal(s)', count($result))); $grouped = $this->groupByCurrency($result); - // TODO save as statistic. $this->saveGroupedAsStatistics($account, $start, $end, $type, $grouped); return $grouped; @@ -547,10 +567,12 @@ trait PeriodOverview protected function saveGroupedAsStatistics(Account $account, Carbon $start, Carbon $end, string $type, array $array): void { unset($array['count']); + Log::debug(sprintf('saveGroupedAsStatistics(#%d, %s, %s, "%s", array(%d))', $account->id, $start->format('Y-m-d'), $end->format('Y-m-d'), $type, count($array))); foreach ($array as $entry) { $this->periodStatisticRepo->saveStatistic($account, $entry['currency_id'], $start, $end, $type, $entry['count'], $entry['amount']); } - if(0 === count($array)) { + if (0 === count($array)) { + Log::debug('Save empty statistic.'); $this->periodStatisticRepo->saveStatistic($account, $this->primaryCurrency->id, $start, $end, $type, 0, '0'); } } From 8f24ac4fcd5cea4054f81479a41cf0369c508e7d Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 26 Sep 2025 19:38:26 +0200 Subject: [PATCH 44/91] Remove statistics when creating a new journal. --- .../Events/StoredGroupEventHandler.php | 33 ++++++++++++++----- .../PeriodStatisticRepository.php | 5 +++ .../PeriodStatisticRepositoryInterface.php | 2 ++ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/app/Handlers/Events/StoredGroupEventHandler.php b/app/Handlers/Events/StoredGroupEventHandler.php index 367c4b4d09..b5912ad2b9 100644 --- a/app/Handlers/Events/StoredGroupEventHandler.php +++ b/app/Handlers/Events/StoredGroupEventHandler.php @@ -28,6 +28,7 @@ use FireflyIII\Events\RequestedSendWebhookMessages; use FireflyIII\Events\StoredTransactionGroup; use FireflyIII\Generator\Webhook\MessageGeneratorInterface; use FireflyIII\Models\TransactionJournal; +use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface; use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; use FireflyIII\Services\Internal\Support\CreditRecalculateService; use FireflyIII\TransactionRules\Engine\RuleEngineInterface; @@ -36,6 +37,8 @@ use Illuminate\Support\Facades\Log; /** * Class StoredGroupEventHandler + * + * TODO migrate to observer? */ class StoredGroupEventHandler { @@ -44,6 +47,7 @@ class StoredGroupEventHandler $this->processRules($event); $this->recalculateCredit($event); $this->triggerWebhooks($event); + $this->removePeriodStatistics($event); } /** @@ -58,14 +62,14 @@ class StoredGroupEventHandler } Log::debug('Now in StoredGroupEventHandler::processRules()'); - $journals = $storedGroupEvent->transactionGroup->transactionJournals; - $array = []; + $journals = $storedGroupEvent->transactionGroup->transactionJournals; + $array = []; /** @var TransactionJournal $journal */ foreach ($journals as $journal) { $array[] = $journal->id; } - $journalIds = implode(',', $array); + $journalIds = implode(',', $array); Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds)); // collect rules: @@ -74,10 +78,10 @@ class StoredGroupEventHandler // add the groups to the rule engine. // it should run the rules in the group and cancel the group if necessary. - $groups = $ruleGroupRepository->getRuleGroupsWithRules('store-journal'); + $groups = $ruleGroupRepository->getRuleGroupsWithRules('store-journal'); // create and fire rule engine. - $newRuleEngine = app(RuleEngineInterface::class); + $newRuleEngine = app(RuleEngineInterface::class); $newRuleEngine->setUser($storedGroupEvent->transactionGroup->user); $newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]); $newRuleEngine->setRuleGroups($groups); @@ -86,7 +90,7 @@ class StoredGroupEventHandler private function recalculateCredit(StoredTransactionGroup $event): void { - $group = $event->transactionGroup; + $group = $event->transactionGroup; /** @var CreditRecalculateService $object */ $object = app(CreditRecalculateService::class); @@ -94,20 +98,33 @@ class StoredGroupEventHandler $object->recalculate(); } + private function removePeriodStatistics(StoredTransactionGroup $event): void + { + /** @var PeriodStatisticRepositoryInterface $repository */ + $repository = app(PeriodStatisticRepositoryInterface::class); + /** @var TransactionJournal $journal */ + foreach ($event->transactionGroup->transactionJournals as $journal) { + $source = $journal->transactions()->where('amount', '<', '0')->first(); + $dest = $journal->transactions()->where('amount', '>', '0')->first(); + $repository->deleteStatisticsForModel($source->account, $journal->date); + $repository->deleteStatisticsForModel($dest->account, $journal->date); + } + } + /** * This method processes all webhooks that respond to the "stored transaction group" trigger (100) */ private function triggerWebhooks(StoredTransactionGroup $storedGroupEvent): void { Log::debug(__METHOD__); - $group = $storedGroupEvent->transactionGroup; + $group = $storedGroupEvent->transactionGroup; if (false === $storedGroupEvent->fireWebhooks) { Log::info(sprintf('Will not fire webhooks for transaction group #%d', $group->id)); return; } - $user = $group->user; + $user = $group->user; /** @var MessageGeneratorInterface $engine */ $engine = app(MessageGeneratorInterface::class); diff --git a/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php index 00da6827ee..9036fa1d7f 100644 --- a/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php +++ b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php @@ -74,4 +74,9 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface { return $model->primaryPeriodStatistics()->where('start', '>=', $start)->where('end', '<=', $end)->get(); } + + public function deleteStatisticsForModel(Model $model, Carbon $date): void + { + $model->primaryPeriodStatistics()->where('start', '<=', $date)->where('end', '>=', $date)->delete(); + } } diff --git a/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php b/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php index d26d85d101..6e4f7cf422 100644 --- a/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php +++ b/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php @@ -37,4 +37,6 @@ interface PeriodStatisticRepositoryInterface public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic; public function allInRangeForModel(Model $model, Carbon $start, Carbon $end): Collection; + + public function deleteStatisticsForModel(Model $model, Carbon $date): void; } From 853a99852ee8b108305bbbb2099b3704e6f53ca3 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 26 Sep 2025 19:39:18 +0200 Subject: [PATCH 45/91] Also remove them when updating transactions. --- .../Events/UpdatedGroupEventHandler.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/app/Handlers/Events/UpdatedGroupEventHandler.php b/app/Handlers/Events/UpdatedGroupEventHandler.php index 973442abb3..eedb842a10 100644 --- a/app/Handlers/Events/UpdatedGroupEventHandler.php +++ b/app/Handlers/Events/UpdatedGroupEventHandler.php @@ -26,11 +26,13 @@ namespace FireflyIII\Handlers\Events; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Enums\WebhookTrigger; use FireflyIII\Events\RequestedSendWebhookMessages; +use FireflyIII\Events\StoredTransactionGroup; use FireflyIII\Events\UpdatedTransactionGroup; use FireflyIII\Generator\Webhook\MessageGeneratorInterface; use FireflyIII\Models\Account; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; +use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface; use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; use FireflyIII\Services\Internal\Support\CreditRecalculateService; use FireflyIII\Support\Models\AccountBalanceCalculator; @@ -49,10 +51,25 @@ class UpdatedGroupEventHandler $this->processRules($event); $this->recalculateCredit($event); $this->triggerWebhooks($event); + $this->removePeriodStatistics($event); if ($event->runRecalculations) { $this->updateRunningBalance($event); } + + } + + private function removePeriodStatistics(UpdatedTransactionGroup $event): void + { + /** @var PeriodStatisticRepositoryInterface $repository */ + $repository = app(PeriodStatisticRepositoryInterface::class); + /** @var TransactionJournal $journal */ + foreach ($event->transactionGroup->transactionJournals as $journal) { + $source = $journal->transactions()->where('amount', '<', '0')->first(); + $dest = $journal->transactions()->where('amount', '>', '0')->first(); + $repository->deleteStatisticsForModel($source->account, $journal->date); + $repository->deleteStatisticsForModel($dest->account, $journal->date); + } } /** From d3c557ca2255ef09ef1fdb3b9d34e08b665f90b9 Mon Sep 17 00:00:00 2001 From: JC5 Date: Fri, 26 Sep 2025 19:43:39 +0200 Subject: [PATCH 46/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-09-26?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Events/StoredGroupEventHandler.php | 17 +- .../Events/UpdatedGroupEventHandler.php | 2 +- app/Models/PeriodStatistic.php | 6 +- .../Account/AccountRepository.php | 1 + .../PeriodStatisticRepository.php | 34 +- app/Support/Amount.php | 56 +-- .../Authentication/RemoteUserGuard.php | 14 +- app/Support/Balance.php | 21 +- app/Support/Binder/AccountList.php | 22 +- app/Support/Binder/BudgetList.php | 16 +- app/Support/Binder/CategoryList.php | 12 +- app/Support/Binder/Date.php | 20 +- app/Support/Binder/JournalList.php | 4 +- app/Support/Binder/TagList.php | 9 +- app/Support/Binder/TagOrId.php | 2 +- app/Support/Binder/UserGroupAccount.php | 5 +- app/Support/Binder/UserGroupBill.php | 5 +- app/Support/Binder/UserGroupExchangeRate.php | 5 +- app/Support/Binder/UserGroupTransaction.php | 5 +- app/Support/CacheProperties.php | 3 +- app/Support/Calendar/Calculator.php | 2 +- .../Chart/Budget/FrontpageChartGenerator.php | 8 +- .../Category/FrontpageChartGenerator.php | 12 +- .../Category/WholePeriodChartGenerator.php | 32 +- app/Support/Chart/ChartData.php | 2 +- app/Support/ChartColour.php | 2 +- app/Support/Cronjobs/AutoBudgetCronjob.php | 2 +- app/Support/Cronjobs/BillWarningCronjob.php | 2 +- app/Support/Cronjobs/ExchangeRatesCronjob.php | 2 +- app/Support/Cronjobs/RecurringCronjob.php | 2 +- app/Support/Cronjobs/UpdateCheckCronjob.php | 12 +- app/Support/ExpandedForm.php | 28 +- app/Support/Export/ExportDataGenerator.php | 80 ++--- app/Support/FireflyConfig.php | 10 +- app/Support/Form/AccountForm.php | 14 +- app/Support/Form/CurrencyForm.php | 32 +- app/Support/Form/FormSupport.php | 10 +- app/Support/Form/PiggyBankForm.php | 8 +- app/Support/Form/RuleForm.php | 12 +- .../Http/Api/AccountBalanceGrouped.php | 40 +-- .../Http/Api/ExchangeRateConverter.php | 33 +- .../Http/Api/SummaryBalanceGrouped.php | 16 +- .../Http/Api/ValidatesUserGroupTrait.php | 10 +- app/Support/Http/Controllers/AugumentData.php | 42 +-- .../Http/Controllers/ChartGeneration.php | 32 +- app/Support/Http/Controllers/CreateStuff.php | 3 +- .../Http/Controllers/DateCalculation.php | 8 +- .../Http/Controllers/GetConfigurationData.php | 34 +- .../Http/Controllers/ModelInformation.php | 34 +- .../Http/Controllers/PeriodOverview.php | 233 ++++++------- .../Http/Controllers/RenderPartialViews.php | 38 +-- .../Http/Controllers/RequestInformation.php | 15 +- .../Http/Controllers/RuleManagement.php | 6 +- .../Controllers/TransactionCalculation.php | 26 +- .../Http/Controllers/UserNavigation.php | 8 +- .../JsonApi/Enrichments/AccountEnrichment.php | 95 +++--- .../Enrichments/AvailableBudgetEnrichment.php | 2 +- .../JsonApi/Enrichments/BudgetEnrichment.php | 26 +- .../Enrichments/BudgetLimitEnrichment.php | 25 +- .../Enrichments/CategoryEnrichment.php | 15 +- .../Enrichments/EnrichmentInterface.php | 2 +- .../Enrichments/PiggyBankEnrichment.php | 62 ++-- .../Enrichments/PiggyBankEventEnrichment.php | 20 +- .../Enrichments/RecurringEnrichment.php | 106 +++--- .../Enrichments/SubscriptionEnrichment.php | 79 ++--- .../TransactionGroupEnrichment.php | 66 ++-- .../JsonApi/Enrichments/WebhookEnrichment.php | 2 +- .../Models/AccountBalanceCalculator.php | 46 +-- app/Support/Models/BillDateCalculator.php | 16 +- app/Support/Models/ReturnsIntegerIdTrait.php | 2 +- .../Models/ReturnsIntegerUserIdTrait.php | 4 +- app/Support/Navigation.php | 67 ++-- .../RecalculatesAvailableBudgetsTrait.php | 36 +- app/Support/ParseDateString.php | 15 +- app/Support/Preferences.php | 56 +-- .../Report/Budget/BudgetReportGenerator.php | 94 ++--- .../Category/CategoryReportGenerator.php | 22 +- .../Summarizer/TransactionSummarizer.php | 22 +- .../Recurring/CalculateRangeOccurrences.php | 14 +- .../Recurring/CalculateXOccurrences.php | 20 +- .../Recurring/CalculateXOccurrencesSince.php | 22 +- .../Recurring/FiltersWeekends.php | 6 +- .../UserGroup/UserGroupInterface.php | 2 +- .../Repositories/UserGroup/UserGroupTrait.php | 11 +- app/Support/Request/AppendsLocationData.php | 18 +- app/Support/Request/ChecksLogin.php | 6 +- app/Support/Request/ConvertsDataTypes.php | 9 +- app/Support/Request/ValidatesWebhooks.php | 6 +- app/Support/Search/AccountSearch.php | 18 +- app/Support/Search/OperatorQuerySearch.php | 320 +++++++++--------- .../Search/QueryParser/GdbotsQueryParser.php | 7 +- .../Search/QueryParser/QueryParser.php | 6 +- .../Singleton/PreferencesSingleton.php | 2 +- app/Support/Steam.php | 177 +++++----- app/Support/System/OAuthKeys.php | 7 +- app/Support/Twig/AmountFormat.php | 6 +- app/Support/Twig/General.php | 19 +- app/Support/Twig/Rule.php | 6 +- app/Support/Twig/TransactionGroupTwig.php | 50 +-- app/Support/Twig/Translation.php | 2 +- composer.lock | 14 +- config/firefly.php | 4 +- package-lock.json | 6 +- 103 files changed, 1411 insertions(+), 1336 deletions(-) diff --git a/app/Handlers/Events/StoredGroupEventHandler.php b/app/Handlers/Events/StoredGroupEventHandler.php index b5912ad2b9..9b4f75b4b3 100644 --- a/app/Handlers/Events/StoredGroupEventHandler.php +++ b/app/Handlers/Events/StoredGroupEventHandler.php @@ -62,14 +62,14 @@ class StoredGroupEventHandler } Log::debug('Now in StoredGroupEventHandler::processRules()'); - $journals = $storedGroupEvent->transactionGroup->transactionJournals; - $array = []; + $journals = $storedGroupEvent->transactionGroup->transactionJournals; + $array = []; /** @var TransactionJournal $journal */ foreach ($journals as $journal) { $array[] = $journal->id; } - $journalIds = implode(',', $array); + $journalIds = implode(',', $array); Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds)); // collect rules: @@ -78,10 +78,10 @@ class StoredGroupEventHandler // add the groups to the rule engine. // it should run the rules in the group and cancel the group if necessary. - $groups = $ruleGroupRepository->getRuleGroupsWithRules('store-journal'); + $groups = $ruleGroupRepository->getRuleGroupsWithRules('store-journal'); // create and fire rule engine. - $newRuleEngine = app(RuleEngineInterface::class); + $newRuleEngine = app(RuleEngineInterface::class); $newRuleEngine->setUser($storedGroupEvent->transactionGroup->user); $newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]); $newRuleEngine->setRuleGroups($groups); @@ -90,7 +90,7 @@ class StoredGroupEventHandler private function recalculateCredit(StoredTransactionGroup $event): void { - $group = $event->transactionGroup; + $group = $event->transactionGroup; /** @var CreditRecalculateService $object */ $object = app(CreditRecalculateService::class); @@ -102,6 +102,7 @@ class StoredGroupEventHandler { /** @var PeriodStatisticRepositoryInterface $repository */ $repository = app(PeriodStatisticRepositoryInterface::class); + /** @var TransactionJournal $journal */ foreach ($event->transactionGroup->transactionJournals as $journal) { $source = $journal->transactions()->where('amount', '<', '0')->first(); @@ -117,14 +118,14 @@ class StoredGroupEventHandler private function triggerWebhooks(StoredTransactionGroup $storedGroupEvent): void { Log::debug(__METHOD__); - $group = $storedGroupEvent->transactionGroup; + $group = $storedGroupEvent->transactionGroup; if (false === $storedGroupEvent->fireWebhooks) { Log::info(sprintf('Will not fire webhooks for transaction group #%d', $group->id)); return; } - $user = $group->user; + $user = $group->user; /** @var MessageGeneratorInterface $engine */ $engine = app(MessageGeneratorInterface::class); diff --git a/app/Handlers/Events/UpdatedGroupEventHandler.php b/app/Handlers/Events/UpdatedGroupEventHandler.php index eedb842a10..ec5ada671d 100644 --- a/app/Handlers/Events/UpdatedGroupEventHandler.php +++ b/app/Handlers/Events/UpdatedGroupEventHandler.php @@ -26,7 +26,6 @@ namespace FireflyIII\Handlers\Events; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Enums\WebhookTrigger; use FireflyIII\Events\RequestedSendWebhookMessages; -use FireflyIII\Events\StoredTransactionGroup; use FireflyIII\Events\UpdatedTransactionGroup; use FireflyIII\Generator\Webhook\MessageGeneratorInterface; use FireflyIII\Models\Account; @@ -63,6 +62,7 @@ class UpdatedGroupEventHandler { /** @var PeriodStatisticRepositoryInterface $repository */ $repository = app(PeriodStatisticRepositoryInterface::class); + /** @var TransactionJournal $journal */ foreach ($event->transactionGroup->transactionJournals as $journal) { $source = $journal->transactions()->where('amount', '<', '0')->first(); diff --git a/app/Models/PeriodStatistic.php b/app/Models/PeriodStatistic.php index 194073bc88..ca166993ac 100644 --- a/app/Models/PeriodStatistic.php +++ b/app/Models/PeriodStatistic.php @@ -22,8 +22,8 @@ class PeriodStatistic extends Model 'created_at' => 'datetime', 'updated_at' => 'datetime', 'deleted_at' => 'datetime', - 'start' => SeparateTimezoneCaster::class, - 'end' => SeparateTimezoneCaster::class, + 'start' => SeparateTimezoneCaster::class, + 'end' => SeparateTimezoneCaster::class, ]; } @@ -54,6 +54,4 @@ class PeriodStatistic extends Model return $this->morphTo(); } - - } diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index caf928b771..8eacb438b4 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -547,6 +547,7 @@ class AccountRepository implements AccountRepositoryInterface, UserGroupInterfac public function periodCollection(Account $account, Carbon $start, Carbon $end): array { Log::debug(sprintf('periodCollection(#%d, %s, %s)', $account->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); + return $account->transactions() ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') diff --git a/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php index 9036fa1d7f..612773a9e2 100644 --- a/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php +++ b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php @@ -34,24 +34,26 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface public function findPeriodStatistics(Model $model, Carbon $start, Carbon $end, array $types): Collection { return $model->primaryPeriodStatistics() - ->where('start', $start) - ->where('end', $end) - ->whereIn('type', $types) - ->get(); + ->where('start', $start) + ->where('end', $end) + ->whereIn('type', $types) + ->get() + ; } public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection { return $model->primaryPeriodStatistics() - ->where('start', $start) - ->where('end', $end) - ->where('type', $type) - ->get(); + ->where('start', $start) + ->where('end', $end) + ->where('type', $type) + ->get() + ; } public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic { - $stat = new PeriodStatistic(); + $stat = new PeriodStatistic(); $stat->primaryStatable()->associate($model); $stat->transaction_currency_id = $currencyId; $stat->start = $start; @@ -63,9 +65,17 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface $stat->type = $type; $stat->save(); - Log::debug(sprintf('Saved #%d [currency #%d, Model %s #%d, %s to %s, %d, %s] as new statistic.', - $stat->id, get_class($model), $model->id, $stat->transaction_currency_id, $stat->start->toW3cString(), $stat->end->toW3cString(), $count, $amount - )); + Log::debug(sprintf( + 'Saved #%d [currency #%d, Model %s #%d, %s to %s, %d, %s] as new statistic.', + $stat->id, + get_class($model), + $model->id, + $stat->transaction_currency_id, + $stat->start->toW3cString(), + $stat->end->toW3cString(), + $count, + $amount + )); return $stat; } diff --git a/app/Support/Amount.php b/app/Support/Amount.php index 4b59c3a4d9..98e8aa284c 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -59,11 +59,11 @@ class Amount // there are five possible positions for the "+" or "-" sign (if it is even used) // pos_a and pos_e could be the ( and ) symbol. - $posA = ''; // before everything - $posB = ''; // before currency symbol - $posC = ''; // after currency symbol - $posD = ''; // before amount - $posE = ''; // after everything + $posA = ''; // before everything + $posB = ''; // before currency symbol + $posC = ''; // after currency symbol + $posD = ''; // before amount + $posE = ''; // after everything // format would be (currency before amount) // AB%sC_D%vE @@ -105,10 +105,10 @@ class Amount } if ($csPrecedes) { - return $posA . $posB . '%s' . $posC . $space . $posD . '%v' . $posE; + return $posA.$posB.'%s'.$posC.$space.$posD.'%v'.$posE; } - return $posA . $posD . '%v' . $space . $posB . '%s' . $posC . $posE; + return $posA.$posD.'%v'.$space.$posB.'%s'.$posC.$posE; } public function convertToPrimary(?User $user = null): bool @@ -125,8 +125,8 @@ class Amount return $pref; } - $key = sprintf('convert_to_primary_%d', $user->id); - $pref = $instance->getPreference($key); + $key = sprintf('convert_to_primary_%d', $user->id); + $pref = $instance->getPreference($key); if (null === $pref) { $res = true === Preferences::getForUser($user, 'convert_to_primary', false)->data && true === config('cer.enabled'); $instance->setPreference($key, $res); @@ -163,15 +163,15 @@ class Amount */ public function formatFlat(string $symbol, int $decimalPlaces, string $amount, ?bool $coloured = null): string { - $locale = Steam::getLocale(); - $rounded = Steam::bcround($amount, $decimalPlaces); + $locale = Steam::getLocale(); + $rounded = Steam::bcround($amount, $decimalPlaces); $coloured ??= true; - $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY); + $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY); $fmt->setSymbol(NumberFormatter::CURRENCY_SYMBOL, $symbol); $fmt->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, $decimalPlaces); $fmt->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, $decimalPlaces); - $result = (string)$fmt->format((float)$rounded); // intentional float + $result = (string)$fmt->format((float)$rounded); // intentional float if (true === $coloured) { if (1 === bccomp($rounded, '0')) { @@ -218,16 +218,16 @@ class Amount */ public function getAmountFromJournalObject(TransactionJournal $journal): string { - $convertToPrimary = $this->convertToPrimary(); - $currency = $this->getPrimaryCurrency(); - $field = $convertToPrimary && $currency->id !== $journal->transaction_currency_id ? 'pc_amount' : 'amount'; + $convertToPrimary = $this->convertToPrimary(); + $currency = $this->getPrimaryCurrency(); + $field = $convertToPrimary && $currency->id !== $journal->transaction_currency_id ? 'pc_amount' : 'amount'; /** @var null|Transaction $sourceTransaction */ $sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first(); if (null === $sourceTransaction) { return '0'; } - $amount = $sourceTransaction->{$field} ?? '0'; + $amount = $sourceTransaction->{$field} ?? '0'; if ((int)$sourceTransaction->foreign_currency_id === $currency->id) { // use foreign amount instead! $amount = (string)$sourceTransaction->foreign_amount; // hard coded to be foreign amount. @@ -284,7 +284,7 @@ class Amount public function getPrimaryCurrencyByUserGroup(UserGroup $userGroup): TransactionCurrency { - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty('getPrimaryCurrencyByGroup'); $cache->addProperty($userGroup->id); if ($cache->has()) { @@ -314,7 +314,7 @@ class Amount $key = sprintf('transaction_currency_%s', $code); /** @var null|TransactionCurrency $pref */ - $pref = $instance->getPreference($key); + $pref = $instance->getPreference($key); if (null !== $pref) { return $pref; } @@ -336,7 +336,7 @@ class Amount $key = sprintf('transaction_currency_%d', $currencyId); /** @var null|TransactionCurrency $pref */ - $pref = $instance->getPreference($key); + $pref = $instance->getPreference($key); if (null !== $pref) { return $pref; } @@ -364,20 +364,20 @@ class Amount private function getLocaleInfo(): array { // get config from preference, not from translation: - $locale = Steam::getLocale(); - $array = Steam::getLocaleArray($locale); + $locale = Steam::getLocale(); + $array = Steam::getLocaleArray($locale); setlocale(LC_MONETARY, $array); - $info = localeconv(); + $info = localeconv(); // correct variables - $info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes'); - $info['p_cs_precedes'] = $this->getLocaleField($info, 'p_cs_precedes'); + $info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes'); + $info['p_cs_precedes'] = $this->getLocaleField($info, 'p_cs_precedes'); - $info['n_sep_by_space'] = $this->getLocaleField($info, 'n_sep_by_space'); - $info['p_sep_by_space'] = $this->getLocaleField($info, 'p_sep_by_space'); + $info['n_sep_by_space'] = $this->getLocaleField($info, 'n_sep_by_space'); + $info['p_sep_by_space'] = $this->getLocaleField($info, 'p_sep_by_space'); - $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY); + $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY); $info['mon_decimal_point'] = $fmt->getSymbol(NumberFormatter::MONETARY_SEPARATOR_SYMBOL); $info['mon_thousands_sep'] = $fmt->getSymbol(NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL); diff --git a/app/Support/Authentication/RemoteUserGuard.php b/app/Support/Authentication/RemoteUserGuard.php index 353765af94..1c87806e00 100644 --- a/app/Support/Authentication/RemoteUserGuard.php +++ b/app/Support/Authentication/RemoteUserGuard.php @@ -48,7 +48,7 @@ class RemoteUserGuard implements Guard public function __construct(protected UserProvider $provider, Application $app) { /** @var null|Request $request */ - $request = $app->get('request'); + $request = $app->get('request'); Log::debug(sprintf('Created RemoteUserGuard for %s "%s"', $request?->getMethod(), $request?->getRequestUri())); $this->application = $app; $this->user = null; @@ -63,8 +63,8 @@ class RemoteUserGuard implements Guard return; } // Get the user identifier from $_SERVER or apache filtered headers - $header = config('auth.guard_header', 'REMOTE_USER'); - $userID = request()->server($header) ?? null; + $header = config('auth.guard_header', 'REMOTE_USER'); + $userID = request()->server($header) ?? null; if (function_exists('apache_request_headers')) { Log::debug('Use apache_request_headers to find user ID.'); @@ -83,7 +83,7 @@ class RemoteUserGuard implements Guard $retrievedUser = $this->provider->retrieveById($userID); // store email address if present in header and not already set. - $header = config('auth.guard_email'); + $header = config('auth.guard_email'); if (null !== $header) { $emailAddress = (string)(request()->server($header) ?? apache_request_headers()[$header] ?? null); @@ -99,7 +99,7 @@ class RemoteUserGuard implements Guard } Log::debug(sprintf('Result of getting user from provider: %s', $retrievedUser->email)); - $this->user = $retrievedUser; + $this->user = $retrievedUser; } public function check(): bool @@ -126,14 +126,14 @@ class RemoteUserGuard implements Guard /** * @SuppressWarnings("PHPMD.ShortMethodName") */ - public function id(): int | string | null + public function id(): int|string|null { Log::debug(sprintf('Now at %s', __METHOD__)); return $this->user?->id; } - public function setUser(Authenticatable | User | null $user): void // @phpstan-ignore-line + public function setUser(Authenticatable|User|null $user): void // @phpstan-ignore-line { Log::debug(sprintf('Now at %s', __METHOD__)); if ($user instanceof User) { diff --git a/app/Support/Balance.php b/app/Support/Balance.php index f9d684c5ef..0a9a97d0ee 100644 --- a/app/Support/Balance.php +++ b/app/Support/Balance.php @@ -48,18 +48,19 @@ class Balance return $cache->get(); } - $query = Transaction::whereIn('transactions.account_id', $accounts->pluck('id')->toArray()) - ->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->orderBy('transaction_journals.date', 'desc') - ->orderBy('transaction_journals.order', 'asc') - ->orderBy('transaction_journals.description', 'desc') - ->orderBy('transactions.amount', 'desc') - ->where('transaction_journals.date', '<=', $date); + $query = Transaction::whereIn('transactions.account_id', $accounts->pluck('id')->toArray()) + ->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->orderBy('transaction_journals.date', 'desc') + ->orderBy('transaction_journals.order', 'asc') + ->orderBy('transaction_journals.description', 'desc') + ->orderBy('transactions.amount', 'desc') + ->where('transaction_journals.date', '<=', $date) + ; - $result = $query->get(['transactions.account_id', 'transactions.transaction_currency_id', 'transactions.balance_after']); + $result = $query->get(['transactions.account_id', 'transactions.transaction_currency_id', 'transactions.balance_after']); foreach ($result as $entry) { - $accountId = (int)$entry->account_id; - $currencyId = (int)$entry->transaction_currency_id; + $accountId = (int)$entry->account_id; + $currencyId = (int)$entry->transaction_currency_id; $currencies[$currencyId] ??= Amount::getTransactionCurrencyById($currencyId); $return[$accountId] ??= []; if (array_key_exists($currencyId, $return[$accountId])) { diff --git a/app/Support/Binder/AccountList.php b/app/Support/Binder/AccountList.php index 3d6c48728e..314a0c025f 100644 --- a/app/Support/Binder/AccountList.php +++ b/app/Support/Binder/AccountList.php @@ -43,21 +43,23 @@ class AccountList implements BinderInterface if ('allAssetAccounts' === $value) { /** @var Collection $collection */ $collection = auth()->user()->accounts() - ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') - ->whereIn('account_types.type', [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value]) - ->orderBy('accounts.name', 'ASC') - ->get(['accounts.*']); + ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') + ->whereIn('account_types.type', [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value]) + ->orderBy('accounts.name', 'ASC') + ->get(['accounts.*']) + ; } if ('allAssetAccounts' !== $value) { - $incoming = array_map('\intval', explode(',', $value)); - $list = array_merge(array_unique($incoming), [0]); + $incoming = array_map('\intval', explode(',', $value)); + $list = array_merge(array_unique($incoming), [0]); /** @var Collection $collection */ $collection = auth()->user()->accounts() - ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') - ->whereIn('accounts.id', $list) - ->orderBy('accounts.name', 'ASC') - ->get(['accounts.*']); + ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') + ->whereIn('accounts.id', $list) + ->orderBy('accounts.name', 'ASC') + ->get(['accounts.*']) + ; } if ($collection->count() > 0) { diff --git a/app/Support/Binder/BudgetList.php b/app/Support/Binder/BudgetList.php index 917885a7d0..6526ebd38a 100644 --- a/app/Support/Binder/BudgetList.php +++ b/app/Support/Binder/BudgetList.php @@ -41,12 +41,13 @@ class BudgetList implements BinderInterface if (auth()->check()) { if ('allBudgets' === $value) { return auth()->user()->budgets()->where('active', true) - ->orderBy('order', 'ASC') - ->orderBy('name', 'ASC') - ->get(); + ->orderBy('order', 'ASC') + ->orderBy('name', 'ASC') + ->get() + ; } - $list = array_unique(array_map('\intval', explode(',', $value))); + $list = array_unique(array_map('\intval', explode(',', $value))); if (0 === count($list)) { // @phpstan-ignore-line app('log')->warning('Budget list count is zero, return 404.'); @@ -56,9 +57,10 @@ class BudgetList implements BinderInterface /** @var Collection $collection */ $collection = auth()->user()->budgets() - ->where('active', true) - ->whereIn('id', $list) - ->get(); + ->where('active', true) + ->whereIn('id', $list) + ->get() + ; // add empty budget if applicable. if (in_array(0, $list, true)) { diff --git a/app/Support/Binder/CategoryList.php b/app/Support/Binder/CategoryList.php index cde58f228f..1275481fa3 100644 --- a/app/Support/Binder/CategoryList.php +++ b/app/Support/Binder/CategoryList.php @@ -41,19 +41,21 @@ class CategoryList implements BinderInterface if (auth()->check()) { if ('allCategories' === $value) { return auth()->user()->categories() - ->orderBy('name', 'ASC') - ->get(); + ->orderBy('name', 'ASC') + ->get() + ; } - $list = array_unique(array_map('\intval', explode(',', $value))); + $list = array_unique(array_map('\intval', explode(',', $value))); if (0 === count($list)) { // @phpstan-ignore-line throw new NotFoundHttpException(); } /** @var Collection $collection */ $collection = auth()->user()->categories() - ->whereIn('id', $list) - ->get(); + ->whereIn('id', $list) + ->get() + ; // add empty category if applicable. if (in_array(0, $list, true)) { diff --git a/app/Support/Binder/Date.php b/app/Support/Binder/Date.php index 4dcfb314c8..99c0ce4c17 100644 --- a/app/Support/Binder/Date.php +++ b/app/Support/Binder/Date.php @@ -43,16 +43,16 @@ class Date implements BinderInterface /** @var FiscalHelperInterface $fiscalHelper */ $fiscalHelper = app(FiscalHelperInterface::class); - $magicWords = [ - 'currentMonthStart' => today(config('app.timezone'))->startOfMonth(), - 'currentMonthEnd' => today(config('app.timezone'))->endOfMonth(), - 'currentYearStart' => today(config('app.timezone'))->startOfYear(), - 'currentYearEnd' => today(config('app.timezone'))->endOfYear(), + $magicWords = [ + 'currentMonthStart' => today(config('app.timezone'))->startOfMonth(), + 'currentMonthEnd' => today(config('app.timezone'))->endOfMonth(), + 'currentYearStart' => today(config('app.timezone'))->startOfYear(), + 'currentYearEnd' => today(config('app.timezone'))->endOfYear(), - 'previousMonthStart' => today(config('app.timezone'))->startOfMonth()->subDay()->startOfMonth(), - 'previousMonthEnd' => today(config('app.timezone'))->startOfMonth()->subDay()->endOfMonth(), - 'previousYearStart' => today(config('app.timezone'))->startOfYear()->subDay()->startOfYear(), - 'previousYearEnd' => today(config('app.timezone'))->startOfYear()->subDay()->endOfYear(), + 'previousMonthStart' => today(config('app.timezone'))->startOfMonth()->subDay()->startOfMonth(), + 'previousMonthEnd' => today(config('app.timezone'))->startOfMonth()->subDay()->endOfMonth(), + 'previousYearStart' => today(config('app.timezone'))->startOfYear()->subDay()->startOfYear(), + 'previousYearEnd' => today(config('app.timezone'))->startOfYear()->subDay()->endOfYear(), 'currentFiscalYearStart' => $fiscalHelper->startOfFiscalYear(today(config('app.timezone'))), 'currentFiscalYearEnd' => $fiscalHelper->endOfFiscalYear(today(config('app.timezone'))), @@ -68,7 +68,7 @@ class Date implements BinderInterface try { $result = new Carbon($value); - } catch (InvalidDateException | InvalidFormatException $e) { // @phpstan-ignore-line + } catch (InvalidDateException|InvalidFormatException $e) { // @phpstan-ignore-line $message = sprintf('Could not parse date "%s" for user #%d: %s', $value, auth()->user()->id, $e->getMessage()); app('log')->error($message); diff --git a/app/Support/Binder/JournalList.php b/app/Support/Binder/JournalList.php index 217dd565ed..5eadcc587a 100644 --- a/app/Support/Binder/JournalList.php +++ b/app/Support/Binder/JournalList.php @@ -39,7 +39,7 @@ class JournalList implements BinderInterface public static function routeBinder(string $value, Route $route): array { if (auth()->check()) { - $list = self::parseList($value); + $list = self::parseList($value); // get the journals by using the collector. /** @var GroupCollectorInterface $collector */ @@ -47,7 +47,7 @@ class JournalList implements BinderInterface $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::TRANSFER->value, TransactionTypeEnum::RECONCILIATION->value]); $collector->withCategoryInformation()->withBudgetInformation()->withTagInformation()->withAccountInformation(); $collector->setJournalIds($list); - $result = $collector->getExtractedJournals(); + $result = $collector->getExtractedJournals(); if (0 === count($result)) { throw new NotFoundHttpException(); } diff --git a/app/Support/Binder/TagList.php b/app/Support/Binder/TagList.php index 685087da75..d87c8c69b9 100644 --- a/app/Support/Binder/TagList.php +++ b/app/Support/Binder/TagList.php @@ -43,10 +43,11 @@ class TagList implements BinderInterface if (auth()->check()) { if ('allTags' === $value) { return auth()->user()->tags() - ->orderBy('tag', 'ASC') - ->get(); + ->orderBy('tag', 'ASC') + ->get() + ; } - $list = array_unique(array_map('\strtolower', explode(',', $value))); + $list = array_unique(array_map('\strtolower', explode(',', $value))); app('log')->debug('List of tags is', $list); if (0 === count($list)) { // @phpstan-ignore-line @@ -58,7 +59,7 @@ class TagList implements BinderInterface /** @var TagRepositoryInterface $repository */ $repository = app(TagRepositoryInterface::class); $repository->setUser(auth()->user()); - $allTags = $repository->get(); + $allTags = $repository->get(); $collection = $allTags->filter( static function (Tag $tag) use ($list) { diff --git a/app/Support/Binder/TagOrId.php b/app/Support/Binder/TagOrId.php index ad3a866e1a..e742fb674d 100644 --- a/app/Support/Binder/TagOrId.php +++ b/app/Support/Binder/TagOrId.php @@ -40,7 +40,7 @@ class TagOrId implements BinderInterface $repository = app(TagRepositoryInterface::class); $repository->setUser(auth()->user()); - $result = $repository->findByTag($value); + $result = $repository->findByTag($value); if (null === $result) { $result = $repository->find((int)$value); } diff --git a/app/Support/Binder/UserGroupAccount.php b/app/Support/Binder/UserGroupAccount.php index 47a7af5541..c395655e87 100644 --- a/app/Support/Binder/UserGroupAccount.php +++ b/app/Support/Binder/UserGroupAccount.php @@ -42,8 +42,9 @@ class UserGroupAccount implements BinderInterface /** @var User $user */ $user = auth()->user(); $account = Account::where('id', (int)$value) - ->where('user_group_id', $user->user_group_id) - ->first(); + ->where('user_group_id', $user->user_group_id) + ->first() + ; if (null !== $account) { return $account; } diff --git a/app/Support/Binder/UserGroupBill.php b/app/Support/Binder/UserGroupBill.php index 05eff73b6e..bd2489965e 100644 --- a/app/Support/Binder/UserGroupBill.php +++ b/app/Support/Binder/UserGroupBill.php @@ -42,8 +42,9 @@ class UserGroupBill implements BinderInterface /** @var User $user */ $user = auth()->user(); $currency = Bill::where('id', (int)$value) - ->where('user_group_id', $user->user_group_id) - ->first(); + ->where('user_group_id', $user->user_group_id) + ->first() + ; if (null !== $currency) { return $currency; } diff --git a/app/Support/Binder/UserGroupExchangeRate.php b/app/Support/Binder/UserGroupExchangeRate.php index 1bb8fcc374..862564fde1 100644 --- a/app/Support/Binder/UserGroupExchangeRate.php +++ b/app/Support/Binder/UserGroupExchangeRate.php @@ -39,8 +39,9 @@ class UserGroupExchangeRate implements BinderInterface /** @var User $user */ $user = auth()->user(); $rate = CurrencyExchangeRate::where('id', (int)$value) - ->where('user_group_id', $user->user_group_id) - ->first(); + ->where('user_group_id', $user->user_group_id) + ->first() + ; if (null !== $rate) { return $rate; } diff --git a/app/Support/Binder/UserGroupTransaction.php b/app/Support/Binder/UserGroupTransaction.php index 61add59c73..fbbf5c1f43 100644 --- a/app/Support/Binder/UserGroupTransaction.php +++ b/app/Support/Binder/UserGroupTransaction.php @@ -39,8 +39,9 @@ class UserGroupTransaction implements BinderInterface /** @var User $user */ $user = auth()->user(); $group = TransactionGroup::where('id', (int)$value) - ->where('user_group_id', $user->user_group_id) - ->first(); + ->where('user_group_id', $user->user_group_id) + ->first() + ; if (null !== $group) { return $group; } diff --git a/app/Support/CacheProperties.php b/app/Support/CacheProperties.php index 38f2863e92..e22808ea06 100644 --- a/app/Support/CacheProperties.php +++ b/app/Support/CacheProperties.php @@ -27,6 +27,7 @@ use Carbon\Carbon; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Cache; use JsonException; + use function Safe\json_encode; /** @@ -87,7 +88,7 @@ class CacheProperties private function hash(): void { - $content = ''; + $content = ''; foreach ($this->properties as $property) { try { $content = sprintf('%s%s', $content, json_encode($property, JSON_THROW_ON_ERROR)); diff --git a/app/Support/Calendar/Calculator.php b/app/Support/Calendar/Calculator.php index b6ee2ceebb..ae5c1d7f72 100644 --- a/app/Support/Calendar/Calculator.php +++ b/app/Support/Calendar/Calculator.php @@ -33,7 +33,7 @@ use SplObjectStorage; */ class Calculator { - public const int DEFAULT_INTERVAL = 1; + public const int DEFAULT_INTERVAL = 1; private static ?SplObjectStorage $intervalMap = null; // @phpstan-ignore-line private static array $intervals = []; diff --git a/app/Support/Chart/Budget/FrontpageChartGenerator.php b/app/Support/Chart/Budget/FrontpageChartGenerator.php index e48cc65e42..b5af64fedd 100644 --- a/app/Support/Chart/Budget/FrontpageChartGenerator.php +++ b/app/Support/Chart/Budget/FrontpageChartGenerator.php @@ -181,7 +181,7 @@ class FrontpageChartGenerator Log::debug(sprintf('Processing limit #%d with %s %s', $limit->id, $limit->transactionCurrency->code, $limit->amount)); } - $spent = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection()->push($budget), $currency); + $spent = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection()->push($budget), $currency); Log::debug(sprintf('Spent array has %d entries.', count($spent))); /** @var array $entry */ @@ -208,7 +208,7 @@ class FrontpageChartGenerator */ private function processRow(array $data, Budget $budget, BudgetLimit $limit, array $entry): array { - $title = sprintf('%s (%s)', $budget->name, $entry['currency_name']); + $title = sprintf('%s (%s)', $budget->name, $entry['currency_name']); Log::debug(sprintf('Title is "%s"', $title)); if ($limit->start_date->startOfDay()->ne($this->start->startOfDay()) || $limit->end_date->startOfDay()->ne($this->end->startOfDay())) { $title = sprintf( @@ -219,8 +219,8 @@ class FrontpageChartGenerator $limit->end_date->isoFormat($this->monthAndDayFormat) ); } - $usePrimary = $this->convertToPrimary && $this->default->id !== $limit->transaction_currency_id; - $amount = $limit->amount; + $usePrimary = $this->convertToPrimary && $this->default->id !== $limit->transaction_currency_id; + $amount = $limit->amount; Log::debug(sprintf('Amount is "%s".', $amount)); if ($usePrimary && $limit->transaction_currency_id !== $this->default->id) { $amount = $limit->native_amount; diff --git a/app/Support/Chart/Category/FrontpageChartGenerator.php b/app/Support/Chart/Category/FrontpageChartGenerator.php index b106deafce..cc9b249235 100644 --- a/app/Support/Chart/Category/FrontpageChartGenerator.php +++ b/app/Support/Chart/Category/FrontpageChartGenerator.php @@ -65,16 +65,16 @@ class FrontpageChartGenerator public function generate(): array { Log::debug(sprintf('Now in %s', __METHOD__)); - $categories = $this->repository->getCategories(); - $accounts = $this->accountRepos->getAccountsByType([AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value]); - $collection = $this->collectExpensesAll($categories, $accounts); + $categories = $this->repository->getCategories(); + $accounts = $this->accountRepos->getAccountsByType([AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value]); + $collection = $this->collectExpensesAll($categories, $accounts); // collect for no-category: - $noCategory = $this->collectNoCatExpenses($accounts); - $collection = array_merge($collection, $noCategory); + $noCategory = $this->collectNoCatExpenses($accounts); + $collection = array_merge($collection, $noCategory); // sort temp array by amount. - $amounts = array_column($collection, 'sum_float'); + $amounts = array_column($collection, 'sum_float'); array_multisort($amounts, SORT_ASC, $collection); $currencyData = $this->createCurrencyGroups($collection); diff --git a/app/Support/Chart/Category/WholePeriodChartGenerator.php b/app/Support/Chart/Category/WholePeriodChartGenerator.php index 044b7f28a2..ce43b1d16e 100644 --- a/app/Support/Chart/Category/WholePeriodChartGenerator.php +++ b/app/Support/Chart/Category/WholePeriodChartGenerator.php @@ -40,22 +40,22 @@ class WholePeriodChartGenerator public function generate(Category $category, Carbon $start, Carbon $end): array { - $collection = new Collection()->push($category); + $collection = new Collection()->push($category); /** @var OperationsRepositoryInterface $opsRepository */ - $opsRepository = app(OperationsRepositoryInterface::class); + $opsRepository = app(OperationsRepositoryInterface::class); /** @var AccountRepositoryInterface $accountRepository */ $accountRepository = app(AccountRepositoryInterface::class); - $types = [AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value]; - $accounts = $accountRepository->getAccountsByType($types); - $step = $this->calculateStep($start, $end); - $chartData = []; - $spent = []; - $earned = []; + $types = [AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value]; + $accounts = $accountRepository->getAccountsByType($types); + $step = $this->calculateStep($start, $end); + $chartData = []; + $spent = []; + $earned = []; - $current = clone $start; + $current = clone $start; while ($current <= $end) { $key = $current->format('Y-m-d'); @@ -65,14 +65,14 @@ class WholePeriodChartGenerator $current = app('navigation')->addPeriod($current, $step, 0); } - $currencies = $this->extractCurrencies($spent) + $this->extractCurrencies($earned); + $currencies = $this->extractCurrencies($spent) + $this->extractCurrencies($earned); // generate chart data (for each currency) /** @var array $currency */ foreach ($currencies as $currency) { - $code = $currency['currency_code']; - $name = $currency['currency_name']; - $chartData[sprintf('spent-in-%s', $code)] = [ + $code = $currency['currency_code']; + $name = $currency['currency_name']; + $chartData[sprintf('spent-in-%s', $code)] = [ 'label' => (string)trans('firefly.box_spent_in_currency', ['currency' => $name]), 'entries' => [], 'type' => 'bar', @@ -87,11 +87,11 @@ class WholePeriodChartGenerator ]; } - $current = clone $start; + $current = clone $start; while ($current <= $end) { - $key = $current->format('Y-m-d'); - $label = app('navigation')->periodShow($current, $step); + $key = $current->format('Y-m-d'); + $label = app('navigation')->periodShow($current, $step); /** @var array $currency */ foreach ($currencies as $currency) { diff --git a/app/Support/Chart/ChartData.php b/app/Support/Chart/ChartData.php index 8d58c6a304..d35242c4d6 100644 --- a/app/Support/Chart/ChartData.php +++ b/app/Support/Chart/ChartData.php @@ -49,7 +49,7 @@ class ChartData if (array_key_exists('primary_currency_id', $data)) { $data['primary_currency_id'] = (string)$data['primary_currency_id']; } - $required = ['start', 'date', 'end', 'entries']; + $required = ['start', 'date', 'end', 'entries']; foreach ($required as $field) { if (!array_key_exists($field, $data)) { throw new FireflyException(sprintf('Data-set is missing the "%s"-variable.', $field)); diff --git a/app/Support/ChartColour.php b/app/Support/ChartColour.php index f08de5c258..9e938b9946 100644 --- a/app/Support/ChartColour.php +++ b/app/Support/ChartColour.php @@ -55,7 +55,7 @@ class ChartColour public static function getColour(int $index): string { $index %= count(self::$colours); - $row = self::$colours[$index]; + $row = self::$colours[$index]; return sprintf('rgba(%d, %d, %d, 0.7)', $row[0], $row[1], $row[2]); } diff --git a/app/Support/Cronjobs/AutoBudgetCronjob.php b/app/Support/Cronjobs/AutoBudgetCronjob.php index 6855884d66..e4e82d2376 100644 --- a/app/Support/Cronjobs/AutoBudgetCronjob.php +++ b/app/Support/Cronjobs/AutoBudgetCronjob.php @@ -70,7 +70,7 @@ class AutoBudgetCronjob extends AbstractCronjob Log::info(sprintf('Will now fire auto budget cron job task for date "%s".', $this->date->format('Y-m-d'))); /** @var CreateAutoBudgetLimits $job */ - $job = app(CreateAutoBudgetLimits::class, [$this->date]); + $job = app(CreateAutoBudgetLimits::class, [$this->date]); $job->setDate($this->date); $job->handle(); diff --git a/app/Support/Cronjobs/BillWarningCronjob.php b/app/Support/Cronjobs/BillWarningCronjob.php index f192aa1224..a358d5879e 100644 --- a/app/Support/Cronjobs/BillWarningCronjob.php +++ b/app/Support/Cronjobs/BillWarningCronjob.php @@ -82,7 +82,7 @@ class BillWarningCronjob extends AbstractCronjob Log::info(sprintf('Will now fire bill notification job task for date "%s".', $this->date->format('Y-m-d H:i:s'))); /** @var WarnAboutBills $job */ - $job = app(WarnAboutBills::class); + $job = app(WarnAboutBills::class); $job->setDate($this->date); $job->setForce($this->force); $job->handle(); diff --git a/app/Support/Cronjobs/ExchangeRatesCronjob.php b/app/Support/Cronjobs/ExchangeRatesCronjob.php index 57cb788bc7..889d6e57de 100644 --- a/app/Support/Cronjobs/ExchangeRatesCronjob.php +++ b/app/Support/Cronjobs/ExchangeRatesCronjob.php @@ -71,7 +71,7 @@ class ExchangeRatesCronjob extends AbstractCronjob Log::info(sprintf('Will now fire exchange rates cron job task for date "%s".', $this->date->format('Y-m-d'))); /** @var DownloadExchangeRates $job */ - $job = app(DownloadExchangeRates::class); + $job = app(DownloadExchangeRates::class); $job->setDate($this->date); $job->handle(); diff --git a/app/Support/Cronjobs/RecurringCronjob.php b/app/Support/Cronjobs/RecurringCronjob.php index 1f8654b9a7..5f4e11a4c2 100644 --- a/app/Support/Cronjobs/RecurringCronjob.php +++ b/app/Support/Cronjobs/RecurringCronjob.php @@ -80,7 +80,7 @@ class RecurringCronjob extends AbstractCronjob { Log::info(sprintf('Will now fire recurring cron job task for date "%s".', $this->date->format('Y-m-d H:i:s'))); - $job = new CreateRecurringTransactions($this->date); + $job = new CreateRecurringTransactions($this->date); $job->setForce($this->force); $job->handle(); diff --git a/app/Support/Cronjobs/UpdateCheckCronjob.php b/app/Support/Cronjobs/UpdateCheckCronjob.php index c7681037dd..d9987a73ad 100644 --- a/app/Support/Cronjobs/UpdateCheckCronjob.php +++ b/app/Support/Cronjobs/UpdateCheckCronjob.php @@ -41,8 +41,8 @@ class UpdateCheckCronjob extends AbstractCronjob Log::debug('Now in checkForUpdates()'); // should not check for updates: - $permission = FireflyConfig::get('permission_update_check', -1); - $value = (int)$permission->data; + $permission = FireflyConfig::get('permission_update_check', -1); + $value = (int)$permission->data; if (1 !== $value) { Log::debug('Update check is not enabled.'); // get stuff from job: @@ -56,9 +56,9 @@ class UpdateCheckCronjob extends AbstractCronjob // TODO this is duplicate. /** @var Configuration $lastCheckTime */ - $lastCheckTime = FireflyConfig::get('last_update_check', Carbon::now()->getTimestamp()); - $now = Carbon::now()->getTimestamp(); - $diff = $now - $lastCheckTime->data; + $lastCheckTime = FireflyConfig::get('last_update_check', Carbon::now()->getTimestamp()); + $now = Carbon::now()->getTimestamp(); + $diff = $now - $lastCheckTime->data; Log::debug(sprintf('Last check time is %d, current time is %d, difference is %d', $lastCheckTime->data, $now, $diff)); if ($diff < 604800 && false === $this->force) { // get stuff from job: @@ -71,7 +71,7 @@ class UpdateCheckCronjob extends AbstractCronjob } // last check time was more than a week ago. Log::debug('Have not checked for a new version in a week!'); - $release = $this->getLatestRelease(); + $release = $this->getLatestRelease(); if ('error' === $release['level']) { // get stuff from job: $this->jobFired = true; diff --git a/app/Support/ExpandedForm.php b/app/Support/ExpandedForm.php index 1dbaeb7d8e..fc464fae22 100644 --- a/app/Support/ExpandedForm.php +++ b/app/Support/ExpandedForm.php @@ -43,7 +43,7 @@ class ExpandedForm */ public function amountNoCurrency(string $name, $value = null, ?array $options = null): string { - $options ??= []; + $options ??= []; $label = $this->label($name, $options); $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); @@ -74,8 +74,8 @@ class ExpandedForm */ public function checkbox(string $name, ?int $value = null, $checked = null, ?array $options = null): string { - $options ??= []; - $value ??= 1; + $options ??= []; + $value ??= 1; $options['checked'] = true === $checked; if (app('session')->has('preFilled')) { @@ -83,10 +83,10 @@ class ExpandedForm $options['checked'] = $preFilled[$name] ?? $options['checked']; } - $label = $this->label($name, $options); - $options = $this->expandOptionArray($name, $label, $options); - $classes = $this->getHolderClasses($name); - $value = $this->fillFieldValue($name, $value); + $label = $this->label($name, $options); + $options = $this->expandOptionArray($name, $label, $options); + $classes = $this->getHolderClasses($name); + $value = $this->fillFieldValue($name, $value); unset($options['placeholder'], $options['autocomplete'], $options['class']); @@ -157,10 +157,10 @@ class ExpandedForm public function integer(string $name, $value = null, ?array $options = null): string { $options ??= []; - $label = $this->label($name, $options); - $options = $this->expandOptionArray($name, $label, $options); - $classes = $this->getHolderClasses($name); - $value = $this->fillFieldValue($name, $value); + $label = $this->label($name, $options); + $options = $this->expandOptionArray($name, $label, $options); + $classes = $this->getHolderClasses($name); + $value = $this->fillFieldValue($name, $value); $options['step'] ??= '1'; try { @@ -209,9 +209,9 @@ class ExpandedForm /** @var Model $entry */ foreach ($set as $entry) { // All Eloquent models have an ID - $entryId = $entry->id; - $current = $entry->toArray(); - $title = null; + $entryId = $entry->id; + $current = $entry->toArray(); + $title = null; foreach ($fields as $field) { if (array_key_exists($field, $current) && null === $title) { $title = $current[$field]; diff --git a/app/Support/Export/ExportDataGenerator.php b/app/Support/Export/ExportDataGenerator.php index b70f2a6615..926f371108 100644 --- a/app/Support/Export/ExportDataGenerator.php +++ b/app/Support/Export/ExportDataGenerator.php @@ -89,8 +89,8 @@ class ExportDataGenerator public function __construct() { - $this->accounts = new Collection(); - $this->start = today(config('app.timezone')); + $this->accounts = new Collection(); + $this->start = today(config('app.timezone')); $this->start->subYear(); $this->end = today(config('app.timezone')); $this->exportTransactions = false; @@ -234,7 +234,7 @@ class ExportDataGenerator */ private function exportAccounts(): string { - $header = [ + $header = [ 'user_id', 'account_id', 'created_at', @@ -255,7 +255,7 @@ class ExportDataGenerator ]; /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class); + $repository = app(AccountRepositoryInterface::class); $repository->setUser($this->user); $allAccounts = $repository->getAccountsByType([]); $records = []; @@ -285,7 +285,7 @@ class ExportDataGenerator } // load the CSV document from a string - $csv = Writer::createFromString(); + $csv = Writer::createFromString(); // insert the header try { @@ -318,8 +318,8 @@ class ExportDataGenerator /** @var BillRepositoryInterface $repository */ $repository = app(BillRepositoryInterface::class); $repository->setUser($this->user); - $bills = $repository->getBills(); - $header = [ + $bills = $repository->getBills(); + $header = [ 'user_id', 'bill_id', 'created_at', @@ -333,7 +333,7 @@ class ExportDataGenerator 'skip', 'active', ]; - $records = []; + $records = []; /** @var Bill $bill */ foreach ($bills as $bill) { @@ -354,7 +354,7 @@ class ExportDataGenerator } // load the CSV document from a string - $csv = Writer::createFromString(); + $csv = Writer::createFromString(); // insert the header try { @@ -384,7 +384,7 @@ class ExportDataGenerator */ private function exportBudgets(): string { - $header = [ + $header = [ 'user_id', 'budget_id', 'name', @@ -398,9 +398,9 @@ class ExportDataGenerator $budgetRepos = app(BudgetRepositoryInterface::class); $budgetRepos->setUser($this->user); - $limitRepos = app(BudgetLimitRepositoryInterface::class); - $budgets = $budgetRepos->getBudgets(); - $records = []; + $limitRepos = app(BudgetLimitRepositoryInterface::class); + $budgets = $budgetRepos->getBudgets(); + $records = []; /** @var Budget $budget */ foreach ($budgets as $budget) { @@ -423,7 +423,7 @@ class ExportDataGenerator } // load the CSV document from a string - $csv = Writer::createFromString(); + $csv = Writer::createFromString(); // insert the header try { @@ -453,10 +453,10 @@ class ExportDataGenerator */ private function exportCategories(): string { - $header = ['user_id', 'category_id', 'created_at', 'updated_at', 'name']; + $header = ['user_id', 'category_id', 'created_at', 'updated_at', 'name']; /** @var CategoryRepositoryInterface $catRepos */ - $catRepos = app(CategoryRepositoryInterface::class); + $catRepos = app(CategoryRepositoryInterface::class); $catRepos->setUser($this->user); $records = []; @@ -474,7 +474,7 @@ class ExportDataGenerator } // load the CSV document from a string - $csv = Writer::createFromString(); + $csv = Writer::createFromString(); // insert the header try { @@ -505,14 +505,14 @@ class ExportDataGenerator private function exportPiggies(): string { /** @var PiggyBankRepositoryInterface $piggyRepos */ - $piggyRepos = app(PiggyBankRepositoryInterface::class); + $piggyRepos = app(PiggyBankRepositoryInterface::class); $piggyRepos->setUser($this->user); /** @var AccountRepositoryInterface $accountRepos */ $accountRepos = app(AccountRepositoryInterface::class); $accountRepos->setUser($this->user); - $header = [ + $header = [ 'user_id', 'piggy_bank_id', 'created_at', @@ -528,8 +528,8 @@ class ExportDataGenerator 'order', 'active', ]; - $records = []; - $piggies = $piggyRepos->getPiggyBanks(); + $records = []; + $piggies = $piggyRepos->getPiggyBanks(); /** @var PiggyBank $piggy */ foreach ($piggies as $piggy) { @@ -554,7 +554,7 @@ class ExportDataGenerator } // load the CSV document from a string - $csv = Writer::createFromString(); + $csv = Writer::createFromString(); // insert the header try { @@ -587,7 +587,7 @@ class ExportDataGenerator /** @var RecurringRepositoryInterface $recurringRepos */ $recurringRepos = app(RecurringRepositoryInterface::class); $recurringRepos->setUser($this->user); - $header = [ + $header = [ // recurrence: 'user_id', 'recurrence_id', 'row_contains', 'created_at', 'updated_at', 'type', 'title', 'description', 'first_date', 'repeat_until', 'latest_date', 'repetitions', 'apply_rules', 'active', @@ -596,8 +596,8 @@ class ExportDataGenerator // transactions + meta: 'currency_code', 'foreign_currency_code', 'source_name', 'source_type', 'destination_name', 'destination_type', 'amount', 'foreign_amount', 'category', 'budget', 'piggy_bank', 'tags', ]; - $records = []; - $recurrences = $recurringRepos->get(); + $records = []; + $recurrences = $recurringRepos->get(); /** @var Recurrence $recurrence */ foreach ($recurrences as $recurrence) { @@ -630,7 +630,7 @@ class ExportDataGenerator $piggyBankId = $recurringRepos->getPiggyBank($transaction); $tags = $recurringRepos->getTags($transaction); - $records[] = [ + $records[] = [ // recurrence $this->user->id, $recurrence->id, @@ -646,7 +646,7 @@ class ExportDataGenerator } } // load the CSV document from a string - $csv = Writer::createFromString(); + $csv = Writer::createFromString(); // insert the header try { @@ -683,8 +683,8 @@ class ExportDataGenerator 'action_type', 'action_value', 'action_order', 'action_active', 'action_stop_processing']; $ruleRepos = app(RuleRepositoryInterface::class); $ruleRepos->setUser($this->user); - $rules = $ruleRepos->getAll(); - $records = []; + $rules = $ruleRepos->getAll(); + $records = []; /** @var Rule $rule */ foreach ($rules as $rule) { @@ -723,7 +723,7 @@ class ExportDataGenerator } // load the CSV document from a string - $csv = Writer::createFromString(); + $csv = Writer::createFromString(); // insert the header try { @@ -753,12 +753,12 @@ class ExportDataGenerator */ private function exportTags(): string { - $header = ['user_id', 'tag_id', 'created_at', 'updated_at', 'tag', 'date', 'description', 'latitude', 'longitude', 'zoom_level']; + $header = ['user_id', 'tag_id', 'created_at', 'updated_at', 'tag', 'date', 'description', 'latitude', 'longitude', 'zoom_level']; $tagRepos = app(TagRepositoryInterface::class); $tagRepos->setUser($this->user); - $tags = $tagRepos->get(); - $records = []; + $tags = $tagRepos->get(); + $records = []; /** @var Tag $tag */ foreach ($tags as $tag) { @@ -777,7 +777,7 @@ class ExportDataGenerator } // load the CSV document from a string - $csv = Writer::createFromString(); + $csv = Writer::createFromString(); // insert the header try { @@ -809,26 +809,26 @@ class ExportDataGenerator { Log::debug('Will now export transactions.'); // TODO better place for keys? - $header = ['user_id', 'group_id', 'journal_id', 'created_at', 'updated_at', 'group_title', 'type', 'currency_code', 'amount', 'foreign_currency_code', 'foreign_amount', 'primary_currency_code', 'pc_amount', 'pc_foreign_amount', 'description', 'date', 'source_name', 'source_iban', 'source_type', 'destination_name', 'destination_iban', 'destination_type', 'reconciled', 'category', 'budget', 'bill', 'tags', 'notes']; + $header = ['user_id', 'group_id', 'journal_id', 'created_at', 'updated_at', 'group_title', 'type', 'currency_code', 'amount', 'foreign_currency_code', 'foreign_amount', 'primary_currency_code', 'pc_amount', 'pc_foreign_amount', 'description', 'date', 'source_name', 'source_iban', 'source_type', 'destination_name', 'destination_iban', 'destination_type', 'reconciled', 'category', 'budget', 'bill', 'tags', 'notes']; $metaFields = config('firefly.journal_meta_fields'); $header = array_merge($header, $metaFields); $primary = Amount::getPrimaryCurrency(); - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setUser($this->user); $collector->setRange($this->start, $this->end)->withAccountInformation()->withCategoryInformation()->withBillInformation()->withBudgetInformation()->withTagInformation()->withNotes(); if (0 !== $this->accounts->count()) { $collector->setAccounts($this->accounts); } - $journals = $collector->getExtractedJournals(); + $journals = $collector->getExtractedJournals(); // get repository for meta data: $repository = app(TransactionGroupRepositoryInterface::class); $repository->setUser($this->user); - $records = []; + $records = []; /** @var array $journal */ foreach ($journals as $journal) { @@ -853,7 +853,7 @@ class ExportDataGenerator $pcForeignAmount = null === $journal['pc_foreign_amount'] ? null : Steam::bcround(Steam::negative($journal['pc_foreign_amount']), $primary->decimal_places); } - $records[] = [ + $records[] = [ $journal['user_id'], $journal['transaction_group_id'], $journal['transaction_journal_id'], $journal['created_at']->toAtomString(), $journal['updated_at']->toAtomString(), $journal['transaction_group_title'], $journal['transaction_type_type'], // amounts and currencies $journal['currency_code'], $amount, $journal['foreign_currency_code'], $foreignAmount, $primary->code, $pcAmount, $pcForeignAmount, @@ -878,7 +878,7 @@ class ExportDataGenerator } // load the CSV document from a string - $csv = Writer::createFromString(); + $csv = Writer::createFromString(); // insert the header try { diff --git a/app/Support/FireflyConfig.php b/app/Support/FireflyConfig.php index b79967f8cb..70d4650c6c 100644 --- a/app/Support/FireflyConfig.php +++ b/app/Support/FireflyConfig.php @@ -39,7 +39,7 @@ class FireflyConfig { public function delete(string $name): void { - $fullName = 'ff3-config-' . $name; + $fullName = 'ff3-config-'.$name; if (Cache::has($fullName)) { Cache::forget($fullName); } @@ -53,7 +53,7 @@ class FireflyConfig */ public function get(string $name, mixed $default = null): ?Configuration { - $fullName = 'ff3-config-' . $name; + $fullName = 'ff3-config-'.$name; if (Cache::has($fullName)) { return Cache::get($fullName); } @@ -61,7 +61,7 @@ class FireflyConfig try { /** @var null|Configuration $config */ $config = Configuration::where('name', $name)->first(['id', 'name', 'data']); - } catch (Exception | QueryException $e) { + } catch (Exception|QueryException $e) { throw new FireflyException(sprintf('Could not poll the database: %s', $e->getMessage()), 0, $e); } @@ -146,13 +146,13 @@ class FireflyConfig $item->name = $name; $item->data = $value; $item->save(); - Cache::forget('ff3-config-' . $name); + Cache::forget('ff3-config-'.$name); return $item; } $config->data = $value; $config->save(); - Cache::forget('ff3-config-' . $name); + Cache::forget('ff3-config-'.$name); return $config; } diff --git a/app/Support/Form/AccountForm.php b/app/Support/Form/AccountForm.php index c7a7685061..9b72eb285a 100644 --- a/app/Support/Form/AccountForm.php +++ b/app/Support/Form/AccountForm.php @@ -62,9 +62,9 @@ class AccountForm */ public function activeWithdrawalDestinations(string $name, mixed $value = null, ?array $options = null): string { - $types = [AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::EXPENSE->value]; - $repository = $this->getAccountRepository(); - $grouped = $this->getAccountsGrouped($types, $repository); + $types = [AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::EXPENSE->value]; + $repository = $this->getAccountRepository(); + $grouped = $this->getAccountsGrouped($types, $repository); $cash = $repository->getCashAccount(); $key = (string)trans('firefly.cash_account_type'); @@ -80,15 +80,15 @@ class AccountForm */ public function assetAccountCheckList(string $name, ?array $options = null): string { - $options ??= []; + $options ??= []; $label = $this->label($name, $options); $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); $selected = request()->old($name) ?? []; // get all asset accounts: - $types = [AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value]; - $grouped = $this->getAccountsGrouped($types); + $types = [AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value]; + $grouped = $this->getAccountsGrouped($types); unset($options['class']); @@ -154,7 +154,7 @@ class AccountForm /** @var Account $account */ foreach ($accountList as $account) { - $role = (string)$repository->getMetaValue($account, 'account_role'); + $role = (string)$repository->getMetaValue($account, 'account_role'); if (in_array($account->accountType->type, $liabilityTypes, true)) { $role = sprintf('l_%s', $account->accountType->type); } diff --git a/app/Support/Form/CurrencyForm.php b/app/Support/Form/CurrencyForm.php index 6d24780f19..bf748c6097 100644 --- a/app/Support/Form/CurrencyForm.php +++ b/app/Support/Form/CurrencyForm.php @@ -72,12 +72,12 @@ class CurrencyForm $currencyRepos = app(CurrencyRepositoryInterface::class); // get all currencies: - $list = $currencyRepos->get(); - $array = []; + $list = $currencyRepos->get(); + $array = []; /** @var TransactionCurrency $currency */ foreach ($list as $currency) { - $array[$currency->id] = $currency->name . ' (' . $currency->symbol . ')'; + $array[$currency->id] = $currency->name.' ('.$currency->symbol.')'; } return $this->select($name, $array, $value, $options); @@ -94,14 +94,14 @@ class CurrencyForm $currencyRepos = app(CurrencyRepositoryInterface::class); // get all currencies: - $list = $currencyRepos->get(); - $array = [ + $list = $currencyRepos->get(); + $array = [ 0 => (string)trans('firefly.no_currency'), ]; /** @var TransactionCurrency $currency */ foreach ($list as $currency) { - $array[$currency->id] = $currency->name . ' (' . $currency->symbol . ')'; + $array[$currency->id] = $currency->name.' ('.$currency->symbol.')'; } return $this->select($name, $array, $value, $options); @@ -124,16 +124,16 @@ class CurrencyForm $primaryCurrency = $options['currency'] ?? app('amount')->getPrimaryCurrency(); /** @var Collection $currencies */ - $currencies = app('amount')->getAllCurrencies(); + $currencies = app('amount')->getAllCurrencies(); unset($options['currency'], $options['placeholder']); // perhaps the currency has been sent to us in the field $amount_currency_id_$name (amount_currency_id_amount) - $preFilled = session('preFilled'); + $preFilled = session('preFilled'); if (!is_array($preFilled)) { $preFilled = []; } - $key = 'amount_currency_id_' . $name; - $sentCurrencyId = array_key_exists($key, $preFilled) ? (int)$preFilled[$key] : $primaryCurrency->id; + $key = 'amount_currency_id_'.$name; + $sentCurrencyId = array_key_exists($key, $preFilled) ? (int)$preFilled[$key] : $primaryCurrency->id; app('log')->debug(sprintf('Sent currency ID is %d', $sentCurrencyId)); @@ -153,7 +153,7 @@ class CurrencyForm } try { - $html = view('form.' . $view, compact('primaryCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); + $html = view('form.'.$view, compact('primaryCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { app('log')->debug(sprintf('Could not render currencyField(): %s', $e->getMessage())); $html = 'Could not render currencyField.'; @@ -179,15 +179,15 @@ class CurrencyForm $primaryCurrency = $options['currency'] ?? app('amount')->getPrimaryCurrency(); /** @var Collection $currencies */ - $currencies = app('amount')->getCurrencies(); + $currencies = app('amount')->getCurrencies(); unset($options['currency'], $options['placeholder']); // perhaps the currency has been sent to us in the field $amount_currency_id_$name (amount_currency_id_amount) - $preFilled = session('preFilled'); + $preFilled = session('preFilled'); if (!is_array($preFilled)) { $preFilled = []; } - $key = 'amount_currency_id_' . $name; - $sentCurrencyId = array_key_exists($key, $preFilled) ? (int)$preFilled[$key] : $primaryCurrency->id; + $key = 'amount_currency_id_'.$name; + $sentCurrencyId = array_key_exists($key, $preFilled) ? (int)$preFilled[$key] : $primaryCurrency->id; app('log')->debug(sprintf('Sent currency ID is %d', $sentCurrencyId)); @@ -207,7 +207,7 @@ class CurrencyForm } try { - $html = view('form.' . $view, compact('primaryCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); + $html = view('form.'.$view, compact('primaryCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { app('log')->debug(sprintf('Could not render currencyField(): %s', $e->getMessage())); $html = 'Could not render currencyField.'; diff --git a/app/Support/Form/FormSupport.php b/app/Support/Form/FormSupport.php index e41c785f00..22a580c295 100644 --- a/app/Support/Form/FormSupport.php +++ b/app/Support/Form/FormSupport.php @@ -36,7 +36,7 @@ trait FormSupport { public function multiSelect(string $name, ?array $list = null, mixed $selected = null, ?array $options = null): string { - $list ??= []; + $list ??= []; $label = $this->label($name, $options); $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); @@ -59,7 +59,7 @@ trait FormSupport */ public function select(string $name, ?array $list = null, $selected = null, ?array $options = null): string { - $list ??= []; + $list ??= []; $label = $this->label($name, $options); $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); @@ -81,10 +81,10 @@ trait FormSupport */ protected function expandOptionArray(string $name, $label, ?array $options = null): array { - $options ??= []; + $options ??= []; $name = str_replace('[]', '', $name); $options['class'] = 'form-control'; - $options['id'] = 'ffInput_' . $name; + $options['id'] = 'ffInput_'.$name; $options['autocomplete'] = 'off'; $options['placeholder'] = ucfirst((string)$label); @@ -145,6 +145,6 @@ trait FormSupport } $name = str_replace('[]', '', $name); - return (string)trans('form.' . $name); + return (string)trans('form.'.$name); } } diff --git a/app/Support/Form/PiggyBankForm.php b/app/Support/Form/PiggyBankForm.php index b6233fd0d6..0818d96157 100644 --- a/app/Support/Form/PiggyBankForm.php +++ b/app/Support/Form/PiggyBankForm.php @@ -62,14 +62,14 @@ class PiggyBankForm /** @var PiggyBank $piggy */ foreach ($piggyBanks as $piggy) { - $group = $piggy->objectGroups->first(); - $groupTitle = null; - $groupOrder = 0; + $group = $piggy->objectGroups->first(); + $groupTitle = null; + $groupOrder = 0; if (null !== $group) { $groupTitle = $group->title; $groupOrder = $group->order; } - $subList[$groupOrder] ??= [ + $subList[$groupOrder] ??= [ 'group' => [ 'title' => $groupTitle, ], diff --git a/app/Support/Form/RuleForm.php b/app/Support/Form/RuleForm.php index f635ed9b86..9566f0301f 100644 --- a/app/Support/Form/RuleForm.php +++ b/app/Support/Form/RuleForm.php @@ -41,8 +41,8 @@ class RuleForm $groupRepos = app(RuleGroupRepositoryInterface::class); // get all currencies: - $list = $groupRepos->get(); - $array = []; + $list = $groupRepos->get(); + $array = []; /** @var RuleGroup $group */ foreach ($list as $group) { @@ -57,15 +57,15 @@ class RuleForm */ public function ruleGroupListWithEmpty(string $name, $value = null, ?array $options = null): string { - $options ??= []; + $options ??= []; $options['class'] = 'form-control'; /** @var RuleGroupRepositoryInterface $groupRepos */ - $groupRepos = app(RuleGroupRepositoryInterface::class); + $groupRepos = app(RuleGroupRepositoryInterface::class); // get all currencies: - $list = $groupRepos->get(); - $array = [ + $list = $groupRepos->get(); + $array = [ 0 => (string)trans('firefly.none_in_select_list'), ]; diff --git a/app/Support/Http/Api/AccountBalanceGrouped.php b/app/Support/Http/Api/AccountBalanceGrouped.php index 4c60e00870..e335da23eb 100644 --- a/app/Support/Http/Api/AccountBalanceGrouped.php +++ b/app/Support/Http/Api/AccountBalanceGrouped.php @@ -67,7 +67,7 @@ class AccountBalanceGrouped /** @var array $currency */ foreach ($this->data as $currency) { // income and expense array prepped: - $income = [ + $income = [ 'label' => 'earned', 'currency_id' => (string)$currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], @@ -86,7 +86,7 @@ class AccountBalanceGrouped 'entries' => [], 'pc_entries' => [], ]; - $expense = [ + $expense = [ 'label' => 'spent', 'currency_id' => (string)$currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], @@ -108,22 +108,22 @@ class AccountBalanceGrouped // loop all possible periods between $start and $end, and add them to the correct dataset. $currentStart = clone $this->start; while ($currentStart <= $this->end) { - $key = $currentStart->format($this->carbonFormat); - $label = $currentStart->toAtomString(); + $key = $currentStart->format($this->carbonFormat); + $label = $currentStart->toAtomString(); // normal entries - $income['entries'][$label] = Steam::bcround($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']); - $expense['entries'][$label] = Steam::bcround($currency[$key]['spent'] ?? '0', $currency['currency_decimal_places']); + $income['entries'][$label] = Steam::bcround($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']); + $expense['entries'][$label] = Steam::bcround($currency[$key]['spent'] ?? '0', $currency['currency_decimal_places']); // converted entries $income['pc_entries'][$label] = Steam::bcround($currency[$key]['pc_earned'] ?? '0', $currency['primary_currency_decimal_places']); $expense['pc_entries'][$label] = Steam::bcround($currency[$key]['pc_spent'] ?? '0', $currency['primary_currency_decimal_places']); // next loop - $currentStart = Navigation::addPeriod($currentStart, $this->preferredRange, 0); + $currentStart = Navigation::addPeriod($currentStart, $this->preferredRange, 0); } - $chartData[] = $income; - $chartData[] = $expense; + $chartData[] = $income; + $chartData[] = $expense; } return $chartData; @@ -193,7 +193,7 @@ class AccountBalanceGrouped private function createDefaultDataEntry(array $journal): void { - $currencyId = (int)$journal['currency_id']; + $currencyId = (int)$journal['currency_id']; $this->data[$currencyId] ??= [ 'currency_id' => (string)$currencyId, 'currency_symbol' => $journal['currency_symbol'], @@ -210,8 +210,8 @@ class AccountBalanceGrouped private function createDefaultPeriodEntry(array $journal): void { - $currencyId = (int)$journal['currency_id']; - $period = $journal['date']->format($this->carbonFormat); + $currencyId = (int)$journal['currency_id']; + $period = $journal['date']->format($this->carbonFormat); $this->data[$currencyId][$period] ??= [ 'period' => $period, 'spent' => '0', @@ -268,9 +268,9 @@ class AccountBalanceGrouped private function processJournal(array $journal): void { // format the date according to the period - $period = $journal['date']->format($this->carbonFormat); - $currencyId = (int)$journal['currency_id']; - $currency = $this->findCurrency($currencyId); + $period = $journal['date']->format($this->carbonFormat); + $currencyId = (int)$journal['currency_id']; + $currency = $this->findCurrency($currencyId); // set the array with monetary info, if it does not exist. $this->createDefaultDataEntry($journal); @@ -278,12 +278,12 @@ class AccountBalanceGrouped $this->createDefaultPeriodEntry($journal); // is this journal's amount in- our outgoing? - $key = $this->getDataKey($journal); - $amount = 'spent' === $key ? Steam::negative($journal['amount']) : Steam::positive($journal['amount']); + $key = $this->getDataKey($journal); + $amount = 'spent' === $key ? Steam::negative($journal['amount']) : Steam::positive($journal['amount']); // get conversion rate - $rate = $this->getRate($currency, $journal['date']); - $amountConverted = bcmul($amount, $rate); + $rate = $this->getRate($currency, $journal['date']); + $amountConverted = bcmul($amount, $rate); // perhaps transaction already has the foreign amount in the primary currency. if ((int)$journal['foreign_currency_id'] === $this->primary->id) { @@ -292,7 +292,7 @@ class AccountBalanceGrouped } // add normal entry - $this->data[$currencyId][$period][$key] = bcadd((string)$this->data[$currencyId][$period][$key], $amount); + $this->data[$currencyId][$period][$key] = bcadd((string)$this->data[$currencyId][$period][$key], $amount); // add converted entry $convertedKey = sprintf('pc_%s', $key); diff --git a/app/Support/Http/Api/ExchangeRateConverter.php b/app/Support/Http/Api/ExchangeRateConverter.php index 84d57a4649..d92313907a 100644 --- a/app/Support/Http/Api/ExchangeRateConverter.php +++ b/app/Support/Http/Api/ExchangeRateConverter.php @@ -128,7 +128,7 @@ class ExchangeRateConverter if ($cache->has()) { return (int)$cache->get(); } - $euro = Amount::getTransactionCurrencyByCode('EUR'); + $euro = Amount::getTransactionCurrencyByCode('EUR'); ++$this->queryCount; $cache->store($euro->id); @@ -144,13 +144,13 @@ class ExchangeRateConverter if ($euroId === $currency->id) { return '1'; } - $rate = $this->getFromDB($currency->id, $euroId, $date->format('Y-m-d')); + $rate = $this->getFromDB($currency->id, $euroId, $date->format('Y-m-d')); if (null !== $rate) { // app('log')->debug(sprintf('Rate for %s to EUR is %s.', $currency->code, $rate)); return $rate; } - $rate = $this->getFromDB($euroId, $currency->id, $date->format('Y-m-d')); + $rate = $this->getFromDB($euroId, $currency->id, $date->format('Y-m-d')); if (null !== $rate) { return bcdiv('1', $rate); // app('log')->debug(sprintf('Inverted rate for %s to EUR is %s.', $currency->code, $rate)); @@ -175,7 +175,7 @@ class ExchangeRateConverter return '1'; } - $key = sprintf('cer-%d-%d-%s', $from, $to, $date); + $key = sprintf('cer-%d-%d-%s', $from, $to, $date); // perhaps the rate has been cached during this particular run $preparedRate = $this->prepared[$date][$from][$to] ?? null; @@ -185,7 +185,7 @@ class ExchangeRateConverter return $preparedRate; } - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($key); if ($cache->has()) { $rate = $cache->get(); @@ -198,14 +198,15 @@ class ExchangeRateConverter } /** @var null|CurrencyExchangeRate $result */ - $result = $this->userGroup->currencyExchangeRates() - ->where('from_currency_id', $from) - ->where('to_currency_id', $to) - ->where('date', '<=', $date) - ->orderBy('date', 'DESC') - ->first(); + $result = $this->userGroup->currencyExchangeRates() + ->where('from_currency_id', $from) + ->where('to_currency_id', $to) + ->where('date', '<=', $date) + ->orderBy('date', 'DESC') + ->first() + ; ++$this->queryCount; - $rate = (string)$result?->rate; + $rate = (string)$result?->rate; if ('' === $rate) { app('log')->debug(sprintf('ExchangeRateConverter: Found no rate for #%d->#%d (%s) in the DB.', $from, $to, $date)); @@ -241,8 +242,8 @@ class ExchangeRateConverter */ private function getRate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string { - $key = $this->getCacheKey($from, $to, $date); - $res = Cache::get($key, null); + $key = $this->getCacheKey($from, $to, $date); + $res = Cache::get($key, null); // find in cache if (null !== $res) { @@ -252,7 +253,7 @@ class ExchangeRateConverter } // find in database - $rate = $this->getFromDB($from->id, $to->id, $date->format('Y-m-d')); + $rate = $this->getFromDB($from->id, $to->id, $date->format('Y-m-d')); if (null !== $rate) { Cache::forever($key, $rate); Log::debug(sprintf('ExchangeRateConverter: Return DB rate from %s to %s on %s.', $from->code, $to->code, $date->format('Y-m-d'))); @@ -261,7 +262,7 @@ class ExchangeRateConverter } // find reverse in database - $rate = $this->getFromDB($to->id, $from->id, $date->format('Y-m-d')); + $rate = $this->getFromDB($to->id, $from->id, $date->format('Y-m-d')); if (null !== $rate) { $rate = bcdiv('1', $rate); Cache::forever($key, $rate); diff --git a/app/Support/Http/Api/SummaryBalanceGrouped.php b/app/Support/Http/Api/SummaryBalanceGrouped.php index 8f88dba7d5..e4fdb41b68 100644 --- a/app/Support/Http/Api/SummaryBalanceGrouped.php +++ b/app/Support/Http/Api/SummaryBalanceGrouped.php @@ -31,7 +31,7 @@ use Illuminate\Support\Facades\Log; class SummaryBalanceGrouped { - private const string SUM = 'sum'; + private const string SUM = 'sum'; private array $amounts = []; private array $currencies; private readonly CurrencyRepositoryInterface $currencyRepository; @@ -48,9 +48,9 @@ class SummaryBalanceGrouped public function groupData(): array { Log::debug('Now going to group data.'); - $return = []; + $return = []; foreach ($this->keys as $key) { - $title = match ($key) { + $title = match ($key) { 'sum' => 'balance', 'expense' => 'spent', 'income' => 'earned', @@ -109,11 +109,11 @@ class SummaryBalanceGrouped /** @var array $journal */ foreach ($journals as $journal) { // transaction info: - $currencyId = (int)$journal['currency_id']; - $amount = bcmul((string)$journal['amount'], $multiplier); - $currency = $this->currencies[$currencyId] ?? Amount::getTransactionCurrencyById($currencyId); - $this->currencies[$currencyId] = $currency; - $pcAmount = $converter->convert($currency, $this->default, $journal['date'], $amount); + $currencyId = (int)$journal['currency_id']; + $amount = bcmul((string)$journal['amount'], $multiplier); + $currency = $this->currencies[$currencyId] ?? Amount::getTransactionCurrencyById($currencyId); + $this->currencies[$currencyId] = $currency; + $pcAmount = $converter->convert($currency, $this->default, $journal['date'], $amount); if ((int)$journal['foreign_currency_id'] === $this->default->id) { // use foreign amount instead $pcAmount = $journal['foreign_amount']; diff --git a/app/Support/Http/Api/ValidatesUserGroupTrait.php b/app/Support/Http/Api/ValidatesUserGroupTrait.php index 0ce08c97ed..3d17a7b42c 100644 --- a/app/Support/Http/Api/ValidatesUserGroupTrait.php +++ b/app/Support/Http/Api/ValidatesUserGroupTrait.php @@ -59,8 +59,8 @@ trait ValidatesUserGroupTrait } /** @var User $user */ - $user = auth()->user(); - $groupId = 0; + $user = auth()->user(); + $groupId = 0; if (!$request->has('user_group_id')) { $groupId = (int)$user->user_group_id; Log::debug(sprintf('validateUserGroup: no user group submitted, use default group #%d.', $groupId)); @@ -71,7 +71,7 @@ trait ValidatesUserGroupTrait } /** @var UserGroupRepositoryInterface $repository */ - $repository = app(UserGroupRepositoryInterface::class); + $repository = app(UserGroupRepositoryInterface::class); $repository->setUser($user); $memberships = $repository->getMembershipsFromGroupId($groupId); @@ -82,14 +82,14 @@ trait ValidatesUserGroupTrait } // need to get the group from the membership: - $group = $repository->getById($groupId); + $group = $repository->getById($groupId); if (null === $group) { Log::debug(sprintf('validateUserGroup: group #%d does not exist.', $groupId)); throw new AuthorizationException((string)trans('validation.belongs_user_or_user_group')); } Log::debug(sprintf('validateUserGroup: validate access of user to group #%d ("%s").', $groupId, $group->title)); - $roles = property_exists($this, 'acceptedRoles') ? $this->acceptedRoles : []; // @phpstan-ignore-line + $roles = property_exists($this, 'acceptedRoles') ? $this->acceptedRoles : []; // @phpstan-ignore-line if (0 === count($roles)) { Log::debug('validateUserGroup: no roles defined, so no access.'); diff --git a/app/Support/Http/Controllers/AugumentData.php b/app/Support/Http/Controllers/AugumentData.php index aa3883244f..2046b7e5b2 100644 --- a/app/Support/Http/Controllers/AugumentData.php +++ b/app/Support/Http/Controllers/AugumentData.php @@ -56,10 +56,10 @@ trait AugumentData /** @var Account $expenseAccount */ foreach ($accounts as $expenseAccount) { - $collection = new Collection(); + $collection = new Collection(); $collection->push($expenseAccount); - $revenue = $repository->findByName($expenseAccount->name, [AccountTypeEnum::REVENUE->value]); + $revenue = $repository->findByName($expenseAccount->name, [AccountTypeEnum::REVENUE->value]); if (null !== $revenue) { $collection->push($revenue); } @@ -116,7 +116,7 @@ trait AugumentData $return[$accountId] = $grouped[$accountId][0]['name']; } } - $return[0] = '(no name)'; + $return[0] = '(no name)'; return $return; } @@ -136,7 +136,7 @@ trait AugumentData $return[$budgetId] = $grouped[$budgetId][0]['name']; } } - $return[0] = (string)trans('firefly.no_budget'); + $return[0] = (string)trans('firefly.no_budget'); return $return; } @@ -158,7 +158,7 @@ trait AugumentData $return[$categoryId] = $grouped[$categoryId][0]['name']; } } - $return[0] = (string)trans('firefly.no_category'); + $return[0] = (string)trans('firefly.no_category'); return $return; } @@ -171,14 +171,14 @@ trait AugumentData Log::debug('In getLimits'); /** @var OperationsRepositoryInterface $opsRepository */ - $opsRepository = app(OperationsRepositoryInterface::class); + $opsRepository = app(OperationsRepositoryInterface::class); /** @var BudgetLimitRepositoryInterface $blRepository */ - $blRepository = app(BudgetLimitRepositoryInterface::class); + $blRepository = app(BudgetLimitRepositoryInterface::class); $end->endOfMonth(); // properties for cache - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty($budget->id); @@ -189,25 +189,25 @@ trait AugumentData return $cache->get(); } - $set = $blRepository->getBudgetLimits($budget, $start, $end); + $set = $blRepository->getBudgetLimits($budget, $start, $end); $budgetCollection = new Collection()->push($budget); // merge sets based on a key, in case of convert to primary currency - $limits = new Collection(); + $limits = new Collection(); /** @var BudgetLimit $entry */ foreach ($set as $entry) { Log::debug(sprintf('Now at budget limit #%d', $entry->id)); - $currency = $entry->transactionCurrency; + $currency = $entry->transactionCurrency; if ($this->convertToPrimary) { // the sumExpenses method already handles this. $currency = $this->primaryCurrency; } // 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; @@ -219,9 +219,9 @@ trait AugumentData $entry->pc_spent = $spent; // normal amount: - $expenses = $opsRepository->sumExpenses($currentStart, $currentEnd, null, $budgetCollection, $entry->transactionCurrency, false); - $spent = $expenses[$entry->transactionCurrency->id]['sum'] ?? '0'; - $entry->spent = $spent; + $expenses = $opsRepository->sumExpenses($currentStart, $currentEnd, null, $budgetCollection, $entry->transactionCurrency, false); + $spent = $expenses[$entry->transactionCurrency->id]['sum'] ?? '0'; + $entry->spent = $spent; $limits->push($entry); } @@ -240,7 +240,7 @@ trait AugumentData /** @var array $journal */ foreach ($array as $journal) { - $name = '(no name)'; + $name = '(no name)'; if (TransactionTypeEnum::WITHDRAWAL->value === $journal['transaction_type_type']) { $name = $journal['destination_account_name']; } @@ -263,16 +263,16 @@ trait AugumentData /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); - $total = $assets->merge($opposing); + $total = $assets->merge($opposing); $collector->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value])->setAccounts($total); - $journals = $collector->getExtractedJournals(); - $sum = [ + $journals = $collector->getExtractedJournals(); + $sum = [ 'grand_sum' => '0', 'per_currency' => [], ]; // loop to support multi currency foreach ($journals as $journal) { - $currencyId = (int)$journal['currency_id']; + $currencyId = (int)$journal['currency_id']; // if not set, set to zero: if (!array_key_exists($currencyId, $sum['per_currency'])) { diff --git a/app/Support/Http/Controllers/ChartGeneration.php b/app/Support/Http/Controllers/ChartGeneration.php index 7fe3b3ab96..c117c51171 100644 --- a/app/Support/Http/Controllers/ChartGeneration.php +++ b/app/Support/Http/Controllers/ChartGeneration.php @@ -59,28 +59,28 @@ trait ChartGeneration return $cache->get(); } Log::debug('Regenerate chart.account.account-balance-chart from scratch.'); - $locale = app('steam')->getLocale(); + $locale = app('steam')->getLocale(); /** @var GeneratorInterface $generator */ - $generator = app(GeneratorInterface::class); + $generator = app(GeneratorInterface::class); /** @var AccountRepositoryInterface $accountRepos */ - $accountRepos = app(AccountRepositoryInterface::class); + $accountRepos = app(AccountRepositoryInterface::class); - $primary = app('amount')->getPrimaryCurrency(); - $chartData = []; + $primary = app('amount')->getPrimaryCurrency(); + $chartData = []; Log::debug(sprintf('Start of accountBalanceChart(list, %s, %s)', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); /** @var Account $account */ foreach ($accounts as $account) { Log::debug(sprintf('Now at account #%d ("%s)', $account->id, $account->name)); - $currency = $accountRepos->getAccountCurrency($account) ?? $primary; - $usePrimary = $convertToPrimary && $primary->id !== $currency->id; - $field = $convertToPrimary ? 'pc_balance' : 'balance'; - $currency = $usePrimary ? $primary : $currency; + $currency = $accountRepos->getAccountCurrency($account) ?? $primary; + $usePrimary = $convertToPrimary && $primary->id !== $currency->id; + $field = $convertToPrimary ? 'pc_balance' : 'balance'; + $currency = $usePrimary ? $primary : $currency; Log::debug(sprintf('Will use field %s', $field)); - $currentSet = [ + $currentSet = [ 'label' => $account->name, 'currency_symbol' => $currency->symbol, 'entries' => [], @@ -91,16 +91,16 @@ trait ChartGeneration $previous = array_values($range)[0]; Log::debug(sprintf('Start balance for account #%d ("%s) is', $account->id, $account->name), $previous); while ($currentStart <= $end) { - $format = $currentStart->format('Y-m-d'); - $label = trim($currentStart->isoFormat((string)trans('config.month_and_day_js', [], $locale))); - $balance = $range[$format] ?? $previous; - $previous = $balance; + $format = $currentStart->format('Y-m-d'); + $label = trim($currentStart->isoFormat((string)trans('config.month_and_day_js', [], $locale))); + $balance = $range[$format] ?? $previous; + $previous = $balance; $currentStart->addDay(); $currentSet['entries'][$label] = $balance[$field] ?? '0'; } - $chartData[] = $currentSet; + $chartData[] = $currentSet; } - $data = $generator->multiSet($chartData); + $data = $generator->multiSet($chartData); $cache->store($data); return $data; diff --git a/app/Support/Http/Controllers/CreateStuff.php b/app/Support/Http/Controllers/CreateStuff.php index c54d585328..a69c44ac54 100644 --- a/app/Support/Http/Controllers/CreateStuff.php +++ b/app/Support/Http/Controllers/CreateStuff.php @@ -32,6 +32,7 @@ use FireflyIII\User; use Illuminate\Support\Facades\Log; use Laravel\Passport\Passport; use phpseclib3\Crypt\RSA; + use function Safe\file_put_contents; /** @@ -103,7 +104,7 @@ trait CreateStuff return; } - $key = RSA::createKey(4096); + $key = RSA::createKey(4096); Log::alert('NO OAuth keys were found. They have been created.'); diff --git a/app/Support/Http/Controllers/DateCalculation.php b/app/Support/Http/Controllers/DateCalculation.php index 83baefcf8b..69c30c77d3 100644 --- a/app/Support/Http/Controllers/DateCalculation.php +++ b/app/Support/Http/Controllers/DateCalculation.php @@ -90,19 +90,19 @@ trait DateCalculation protected function getNextPeriods(Carbon $date, string $range): array { // select thing for next 12 periods: - $loop = []; + $loop = []; /** @var Carbon $current */ $current = app('navigation')->startOfPeriod($date, $range); $current = app('navigation')->endOfPeriod($current, $range); $current->addDay(); - $count = 0; + $count = 0; while ($count < 12) { $current = app('navigation')->endOfPeriod($current, $range); $currentStart = app('navigation')->startOfPeriod($current, $range); - $loop[] = [ + $loop[] = [ 'label' => $current->format('Y-m-d'), 'title' => app('navigation')->periodShow($current, $range), 'start' => clone $currentStart, @@ -122,7 +122,7 @@ trait DateCalculation protected function getPreviousPeriods(Carbon $date, string $range): array { // select thing for last 12 periods: - $loop = []; + $loop = []; /** @var Carbon $current */ $current = app('navigation')->startOfPeriod($date, $range); diff --git a/app/Support/Http/Controllers/GetConfigurationData.php b/app/Support/Http/Controllers/GetConfigurationData.php index d1005bfdcb..b0bb16b3f1 100644 --- a/app/Support/Http/Controllers/GetConfigurationData.php +++ b/app/Support/Http/Controllers/GetConfigurationData.php @@ -61,13 +61,13 @@ trait GetConfigurationData $steps = []; if (is_array($elements) && count($elements) > 0) { foreach ($elements as $key => $options) { - $currentStep = $options; + $currentStep = $options; // get the text: - $currentStep['intro'] = (string)trans('intro.' . $route . '_' . $key); + $currentStep['intro'] = (string)trans('intro.'.$route.'_'.$key); // save in array: - $steps[] = $currentStep; + $steps[] = $currentStep; } } app('log')->debug(sprintf('Total basic steps for %s is %d', $routeKey, count($steps))); @@ -82,22 +82,22 @@ trait GetConfigurationData */ protected function getDateRangeConfig(): array // get configuration + get preferences. { - $viewRange = app('navigation')->getViewRange(false); + $viewRange = app('navigation')->getViewRange(false); Log::debug(sprintf('dateRange: the view range is "%s"', $viewRange)); /** @var Carbon $start */ - $start = session('start'); + $start = session('start'); /** @var Carbon $end */ - $end = session('end'); + $end = session('end'); /** @var Carbon $first */ - $first = session('first'); - $title = sprintf('%s - %s', $start->isoFormat($this->monthAndDayFormat), $end->isoFormat($this->monthAndDayFormat)); - $isCustom = true === session('is_custom_range', false); - $today = today(config('app.timezone')); - $ranges = [ + $first = session('first'); + $title = sprintf('%s - %s', $start->isoFormat($this->monthAndDayFormat), $end->isoFormat($this->monthAndDayFormat)); + $isCustom = true === session('is_custom_range', false); + $today = today(config('app.timezone')); + $ranges = [ // first range is the current range: $title => [$start, $end], ]; @@ -127,10 +127,10 @@ trait GetConfigurationData // today: /** @var Carbon $todayStart */ - $todayStart = app('navigation')->startOfPeriod($today, $viewRange); + $todayStart = app('navigation')->startOfPeriod($today, $viewRange); /** @var Carbon $todayEnd */ - $todayEnd = app('navigation')->endOfPeriod($todayStart, $viewRange); + $todayEnd = app('navigation')->endOfPeriod($todayStart, $viewRange); if ($todayStart->ne($start) || $todayEnd->ne($end)) { $ranges[ucfirst((string)trans('firefly.today'))] = [$todayStart, $todayEnd]; @@ -186,16 +186,16 @@ trait GetConfigurationData // user is on page with specific instructions: if ('' !== $specificPage) { $routeKey = str_replace('.', '_', $route); - $elements = config(sprintf('intro.%s', $routeKey . '_' . $specificPage)); + $elements = config(sprintf('intro.%s', $routeKey.'_'.$specificPage)); if (is_array($elements) && count($elements) > 0) { foreach ($elements as $key => $options) { - $currentStep = $options; + $currentStep = $options; // get the text: - $currentStep['intro'] = (string)trans('intro.' . $route . '_' . $specificPage . '_' . $key); + $currentStep['intro'] = (string)trans('intro.'.$route.'_'.$specificPage.'_'.$key); // save in array: - $steps[] = $currentStep; + $steps[] = $currentStep; } } } diff --git a/app/Support/Http/Controllers/ModelInformation.php b/app/Support/Http/Controllers/ModelInformation.php index 152c9671ef..093120cf7a 100644 --- a/app/Support/Http/Controllers/ModelInformation.php +++ b/app/Support/Http/Controllers/ModelInformation.php @@ -75,14 +75,14 @@ trait ModelInformation protected function getLiabilityTypes(): array { /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class); + $repository = app(AccountRepositoryInterface::class); // types of liability: /** @var AccountType $debt */ - $debt = $repository->getAccountTypeByType(AccountTypeEnum::DEBT->value); + $debt = $repository->getAccountTypeByType(AccountTypeEnum::DEBT->value); /** @var AccountType $loan */ - $loan = $repository->getAccountTypeByType(AccountTypeEnum::LOAN->value); + $loan = $repository->getAccountTypeByType(AccountTypeEnum::LOAN->value); /** @var AccountType $mortgage */ $mortgage = $repository->getAccountTypeByType(AccountTypeEnum::MORTGAGE->value); @@ -114,8 +114,8 @@ trait ModelInformation protected function getTriggersForBill(Bill $bill): array // get info and argument { // TODO duplicate code - $operators = config('search.operators'); - $triggers = []; + $operators = config('search.operators'); + $triggers = []; foreach ($operators as $key => $operator) { if ('user_action' !== $key && false === $operator['alias']) { $triggers[$key] = (string)trans(sprintf('firefly.rule_trigger_%s_choice', $key)); @@ -165,8 +165,8 @@ trait ModelInformation private function getTriggersForJournal(TransactionJournal $journal): array { // TODO duplicated code. - $operators = config('search.operators'); - $triggers = []; + $operators = config('search.operators'); + $triggers = []; foreach ($operators as $key => $operator) { if ('user_action' !== $key && false === $operator['alias']) { $triggers[$key] = (string)trans(sprintf('firefly.rule_trigger_%s_choice', $key)); @@ -174,18 +174,18 @@ trait ModelInformation } asort($triggers); - $result = []; - $journalTriggers = []; - $values = []; - $index = 0; + $result = []; + $journalTriggers = []; + $values = []; + $index = 0; // amount, description, category, budget, tags, source, destination, notes, currency type // ,type /** @var null|Transaction $source */ - $source = $journal->transactions()->where('amount', '<', 0)->first(); + $source = $journal->transactions()->where('amount', '<', 0)->first(); /** @var null|Transaction $destination */ - $destination = $journal->transactions()->where('amount', '>', 0)->first(); + $destination = $journal->transactions()->where('amount', '>', 0)->first(); if (null === $destination || null === $source) { return $result; } @@ -220,21 +220,21 @@ trait ModelInformation ++$index; // category (if) - $category = $journal->categories()->first(); + $category = $journal->categories()->first(); if (null !== $category) { $journalTriggers[$index] = 'category_is'; $values[$index] = $category->name; ++$index; } // budget (if) - $budget = $journal->budgets()->first(); + $budget = $journal->budgets()->first(); if (null !== $budget) { $journalTriggers[$index] = 'budget_is'; $values[$index] = $budget->name; ++$index; } // tags (if) - $tags = $journal->tags()->get(); + $tags = $journal->tags()->get(); /** @var Tag $tag */ foreach ($tags as $tag) { @@ -243,7 +243,7 @@ trait ModelInformation ++$index; } // notes (if) - $notes = $journal->notes()->first(); + $notes = $journal->notes()->first(); if (null !== $notes) { $journalTriggers[$index] = 'notes_is'; $values[$index] = $notes->text; diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index 9012e471b9..6af3f30b6f 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -88,12 +88,12 @@ trait PeriodOverview $this->accountRepository = app(AccountRepositoryInterface::class); $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); $range = Navigation::getViewRange(true); - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); - $this->statistics = $this->periodStatisticRepo->allInRangeForModel($account, $start, $end); + $dates = Navigation::blockPeriods($start, $end, $range); + [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); + $this->statistics = $this->periodStatisticRepo->allInRangeForModel($account, $start, $end); // TODO needs to be re-arranged: // get all period stats for entire range. @@ -101,7 +101,7 @@ trait PeriodOverview // create new ones, or use collected. - $entries = []; + $entries = []; Log::debug(sprintf('Count of loops: %d', count($dates))); foreach ($dates as $currentDate) { $entries[] = $this->getSingleAccountPeriod($account, $currentDate['period'], $currentDate['start'], $currentDate['end']); @@ -137,11 +137,11 @@ trait PeriodOverview */ protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; // properties for entries with their amounts. - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty($range); @@ -153,32 +153,32 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setCategory($category); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setCategory($category); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setCategory($category); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); - $transferSet = $collector->getExtractedJournals(); + $transferSet = $collector->getExtractedJournals(); foreach ($dates as $currentDate) { $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); $earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']); @@ -186,17 +186,17 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'transactions' => 0, - 'title' => $title, - 'route' => route( - 'categories.show', - [$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] - ), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'transactions' => 0, + 'title' => $title, + 'route' => route( + 'categories.show', + [$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] + ), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } $cache->store($entries); @@ -212,11 +212,11 @@ trait PeriodOverview */ protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty($this->convertToPrimary); @@ -227,28 +227,28 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // get all expenses without a budget. /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end)->withoutBudget()->withAccountInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $journals = $collector->getExtractedJournals(); + $journals = $collector->getExtractedJournals(); foreach ($dates as $currentDate) { $set = $this->filterJournalsByDate($journals, $currentDate['start'], $currentDate['end']); $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'title' => $title, - 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($set), - 'spent' => $this->groupByCurrency($set), - 'earned' => [], - 'transferred_away' => [], - 'transferred_in' => [], - ]; + 'title' => $title, + 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($set), + 'spent' => $this->groupByCurrency($set), + 'earned' => [], + 'transferred_away' => [], + 'transferred_in' => [], + ]; } $cache->store($entries); @@ -265,38 +265,38 @@ trait PeriodOverview protected function getNoCategoryPeriodOverview(Carbon $theDate): array { Log::debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d'))); - $range = Navigation::getViewRange(true); - $first = $this->journalRepos->firstNull(); - $start = null === $first ? new Carbon() : $first->date; - $end = clone $theDate; - $end = Navigation::endOfPeriod($end, $range); + $range = Navigation::getViewRange(true); + $first = $this->journalRepos->firstNull(); + $start = null === $first ? new Carbon() : $first->date; + $end = clone $theDate; + $end = Navigation::endOfPeriod($end, $range); Log::debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d'))); Log::debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d'))); // properties for cache - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); @@ -310,13 +310,13 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'title' => $title, - 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'title' => $title, + 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } Log::debug('End of loops'); @@ -333,7 +333,7 @@ trait PeriodOverview 'total_transactions' => 0, ]; foreach ($types as $type) { - $set = $this->getSingleAccountPeriodByType($account, $start, $end, $type); + $set = $this->getSingleAccountPeriodByType($account, $start, $end, $type); $return['total_transactions'] += $set['count']; unset($set['count']); $return[$type] = $set; @@ -353,6 +353,7 @@ trait PeriodOverview echo sprintf('End: "%s" vs "%s": %s', $statistic->end->toW3cString(), $end->toW3cString(), var_export($statistic->end->eq($end), true)); var_dump($statistic->end); var_dump($end); + exit; } @@ -398,21 +399,21 @@ trait PeriodOverview } // each result must be grouped by currency, then saved as period statistic. Log::debug(sprintf('Going to group %d found journal(s)', count($result))); - $grouped = $this->groupByCurrency($result); + $grouped = $this->groupByCurrency($result); $this->saveGroupedAsStatistics($account, $start, $end, $type, $grouped); return $grouped; } - $grouped = [ + $grouped = [ 'count' => 0, ]; /** @var PeriodStatistic $statistic */ foreach ($statistics as $statistic) { - $id = (int)$statistic->transaction_currency_id; - $currency = Amount::getTransactionCurrencyById($id); - $grouped[$id] = [ + $id = (int)$statistic->transaction_currency_id; + $currency = Amount::getTransactionCurrencyById($id); + $grouped[$id] = [ 'amount' => (string)$statistic->amount, 'count' => (int)$statistic->count, 'currency_id' => $currency->id, @@ -434,11 +435,11 @@ trait PeriodOverview */ protected function getTagPeriodOverview(Tag $tag, Carbon $start, Carbon $end): array // period overview for tags. { - $range = Navigation::getViewRange(true); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; // properties for cache - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('tag-period-entries'); @@ -448,37 +449,37 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); - $transferSet = $collector->getExtractedJournals(); + $transferSet = $collector->getExtractedJournals(); // filer all of them: - $earnedSet = $this->filterJournalsByTag($earnedSet, $tag); - $spentSet = $this->filterJournalsByTag($spentSet, $tag); - $transferSet = $this->filterJournalsByTag($transferSet, $tag); + $earnedSet = $this->filterJournalsByTag($earnedSet, $tag); + $spentSet = $this->filterJournalsByTag($spentSet, $tag); + $transferSet = $this->filterJournalsByTag($transferSet, $tag); foreach ($dates as $currentDate) { $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); @@ -487,17 +488,17 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'transactions' => 0, - 'title' => $title, - 'route' => route( - 'tags.show', - [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] - ), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'transactions' => 0, + 'title' => $title, + 'route' => route( + 'tags.show', + [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] + ), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } return $entries; @@ -508,12 +509,12 @@ trait PeriodOverview */ protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); - $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); + $range = Navigation::getViewRange(true); + $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; // properties for cache - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('transactions-period-entries'); @@ -523,16 +524,16 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - $spent = []; - $earned = []; - $transferred = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; + $spent = []; + $earned = []; + $transferred = []; // collect all journals in this period (regardless of type) - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTypes($types)->setRange($start, $end); - $genericSet = $collector->getExtractedJournals(); - $loops = 0; + $genericSet = $collector->getExtractedJournals(); + $loops = 0; foreach ($dates as $currentDate) { $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); @@ -550,14 +551,14 @@ trait PeriodOverview } } $entries[] - = [ - 'title' => $title, - 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + = [ + 'title' => $title, + 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; ++$loops; } @@ -598,7 +599,7 @@ trait PeriodOverview { $return = []; foreach ($set as $entry) { - $found = false; + $found = false; /** @var array $localTag */ foreach ($entry['tags'] as $localTag) { @@ -713,13 +714,13 @@ trait PeriodOverview exit; } - $currencyId = (int)$journal['currency_id']; - $currencyCode = $journal['currency_code']; - $currencyName = $journal['currency_name']; - $currencySymbol = $journal['currency_symbol']; - $currencyDecimalPlaces = $journal['currency_decimal_places']; - $foreignCurrencyId = $journal['foreign_currency_id']; - $amount = $journal['amount'] ?? '0'; + $currencyId = (int)$journal['currency_id']; + $currencyCode = $journal['currency_code']; + $currencyName = $journal['currency_name']; + $currencySymbol = $journal['currency_symbol']; + $currencyDecimalPlaces = $journal['currency_decimal_places']; + $foreignCurrencyId = $journal['foreign_currency_id']; + $amount = $journal['amount'] ?? '0'; if ($this->convertToPrimary && $currencyId !== $this->primaryCurrency->id && $foreignCurrencyId !== $this->primaryCurrency->id) { $amount = $journal['pc_amount'] ?? '0'; diff --git a/app/Support/Http/Controllers/RenderPartialViews.php b/app/Support/Http/Controllers/RenderPartialViews.php index 05ff99e38b..f7d5df7cb8 100644 --- a/app/Support/Http/Controllers/RenderPartialViews.php +++ b/app/Support/Http/Controllers/RenderPartialViews.php @@ -52,20 +52,20 @@ trait RenderPartialViews protected function budgetEntry(array $attributes): string // generate view for report. { /** @var PopupReportInterface $popupHelper */ - $popupHelper = app(PopupReportInterface::class); + $popupHelper = app(PopupReportInterface::class); /** @var BudgetRepositoryInterface $budgetRepository */ $budgetRepository = app(BudgetRepositoryInterface::class); $budget = $budgetRepository->find((int)$attributes['budgetId']); - $accountRepos = app(AccountRepositoryInterface::class); - $account = $accountRepos->find((int)$attributes['accountId']); + $accountRepos = app(AccountRepositoryInterface::class); + $account = $accountRepos->find((int)$attributes['accountId']); if (null === $budget || null === $account) { throw new FireflyException('Could not render popup.report.balance-amount because budget or account is null.'); } - $journals = $popupHelper->balanceForBudget($budget, $account, $attributes); + $journals = $popupHelper->balanceForBudget($budget, $account, $attributes); try { $view = view('popup.report.balance-amount', compact('journals', 'budget', 'account'))->render(); @@ -113,14 +113,14 @@ trait RenderPartialViews $budgetRepository = app(BudgetRepositoryInterface::class); /** @var PopupReportInterface $popupHelper */ - $popupHelper = app(PopupReportInterface::class); + $popupHelper = app(PopupReportInterface::class); - $budget = $budgetRepository->find((int)$attributes['budgetId']); + $budget = $budgetRepository->find((int)$attributes['budgetId']); if (null === $budget) { // transactions without a budget. $budget = new Budget(); } - $journals = $popupHelper->byBudget($budget, $attributes); + $journals = $popupHelper->byBudget($budget, $attributes); try { $view = view('popup.report.budget-spent-amount', compact('journals', 'budget'))->render(); @@ -142,7 +142,7 @@ trait RenderPartialViews protected function categoryEntry(array $attributes): string // generate view for report. { /** @var PopupReportInterface $popupHelper */ - $popupHelper = app(PopupReportInterface::class); + $popupHelper = app(PopupReportInterface::class); /** @var CategoryRepositoryInterface $categoryRepository */ $categoryRepository = app(CategoryRepositoryInterface::class); @@ -237,15 +237,15 @@ trait RenderPartialViews $accountRepository = app(AccountRepositoryInterface::class); /** @var PopupReportInterface $popupHelper */ - $popupHelper = app(PopupReportInterface::class); + $popupHelper = app(PopupReportInterface::class); - $account = $accountRepository->find((int)$attributes['accountId']); + $account = $accountRepository->find((int)$attributes['accountId']); if (null === $account) { return 'This is an unknown account. Apologies.'; } - $journals = $popupHelper->byExpenses($account, $attributes); + $journals = $popupHelper->byExpenses($account, $attributes); try { $view = view('popup.report.expense-entry', compact('journals', 'account'))->render(); @@ -266,8 +266,8 @@ trait RenderPartialViews */ protected function getCurrentActions(Rule $rule): array // get info from object and present. { - $index = 0; - $actions = []; + $index = 0; + $actions = []; // must be repos $currentActions = $rule->ruleActions()->orderBy('order', 'ASC')->get(); @@ -306,8 +306,8 @@ trait RenderPartialViews protected function getCurrentTriggers(Rule $rule): array // get info from object and present. { // TODO duplicated code. - $operators = config('search.operators'); - $triggers = []; + $operators = config('search.operators'); + $triggers = []; foreach ($operators as $key => $operator) { if ('user_action' !== $key && false === $operator['alias']) { $triggers[$key] = (string)trans(sprintf('firefly.rule_trigger_%s_choice', $key)); @@ -325,7 +325,7 @@ trait RenderPartialViews $count = ($index + 1); try { - $rootOperator = OperatorQuerySearch::getRootOperator((string)$entry->trigger_type); + $rootOperator = OperatorQuerySearch::getRootOperator((string)$entry->trigger_type); if (str_starts_with($rootOperator, '-')) { $rootOperator = substr($rootOperator, 1); } @@ -365,14 +365,14 @@ trait RenderPartialViews $accountRepository = app(AccountRepositoryInterface::class); /** @var PopupReportInterface $popupHelper */ - $popupHelper = app(PopupReportInterface::class); - $account = $accountRepository->find((int)$attributes['accountId']); + $popupHelper = app(PopupReportInterface::class); + $account = $accountRepository->find((int)$attributes['accountId']); if (null === $account) { return 'This is an unknown category. Apologies.'; } - $journals = $popupHelper->byIncome($account, $attributes); + $journals = $popupHelper->byIncome($account, $attributes); try { $view = view('popup.report.income-entry', compact('journals', 'account'))->render(); diff --git a/app/Support/Http/Controllers/RequestInformation.php b/app/Support/Http/Controllers/RequestInformation.php index b7600f778a..39a6df3aff 100644 --- a/app/Support/Http/Controllers/RequestInformation.php +++ b/app/Support/Http/Controllers/RequestInformation.php @@ -35,6 +35,7 @@ use Illuminate\Routing\Route; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Route as RouteFacade; use Illuminate\Support\Facades\Validator; + use function Safe\parse_url; /** @@ -100,13 +101,13 @@ trait RequestInformation $page = $this->getPageName(); $specificPage = $this->getSpecificPageName(); // indicator if user has seen the help for this page ( + special page): - $key = sprintf('shown_demo_%s%s', $page, $specificPage); + $key = sprintf('shown_demo_%s%s', $page, $specificPage); // is there an intro for this route? $intro = config(sprintf('intro.%s', $page)) ?? []; $specialIntro = config(sprintf('intro.%s%s', $page, $specificPage)) ?? []; // some routes have a "what" parameter, which indicates a special page: - $shownDemo = true; + $shownDemo = true; // both must be array and either must be > 0 if (count($intro) > 0 || count($specialIntro) > 0) { $shownDemo = app('preferences')->get($key, false)->data; @@ -127,7 +128,7 @@ trait RequestInformation $start = session('start', today(config('app.timezone'))->startOfMonth()); /** @var Carbon $end */ - $end = session('end', today(config('app.timezone'))->endOfMonth()); + $end = session('end', today(config('app.timezone'))->endOfMonth()); if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) { return true; } @@ -145,20 +146,20 @@ trait RequestInformation final protected function parseAttributes(array $attributes): array // parse input + return result { $attributes['location'] ??= ''; - $attributes['accounts'] = AccountList::routeBinder($attributes['accounts'] ?? '', new Route('get', '', [])); - $date = Carbon::createFromFormat('Ymd', $attributes['startDate']); + $attributes['accounts'] = AccountList::routeBinder($attributes['accounts'] ?? '', new Route('get', '', [])); + $date = Carbon::createFromFormat('Ymd', $attributes['startDate']); if (!$date instanceof Carbon) { $date = today(config('app.timezone')); } $date->startOfMonth(); $attributes['startDate'] = $date; - $date2 = Carbon::createFromFormat('Ymd', $attributes['endDate']); + $date2 = Carbon::createFromFormat('Ymd', $attributes['endDate']); if (!$date2 instanceof Carbon) { $date2 = today(config('app.timezone')); } $date2->endOfDay(); - $attributes['endDate'] = $date2; + $attributes['endDate'] = $date2; return $attributes; } diff --git a/app/Support/Http/Controllers/RuleManagement.php b/app/Support/Http/Controllers/RuleManagement.php index 9903873484..081d1c44d0 100644 --- a/app/Support/Http/Controllers/RuleManagement.php +++ b/app/Support/Http/Controllers/RuleManagement.php @@ -74,8 +74,8 @@ trait RuleManagement protected function getPreviousTriggers(Request $request): array { // TODO duplicated code. - $operators = config('search.operators'); - $triggers = []; + $operators = config('search.operators'); + $triggers = []; foreach ($operators as $key => $operator) { if ('user_action' !== $key && false === $operator['alias']) { $triggers[$key] = (string)trans(sprintf('firefly.rule_trigger_%s_choice', $key)); @@ -129,7 +129,7 @@ trait RuleManagement } asort($triggers); - $index = 0; + $index = 0; foreach ($submittedOperators as $operator) { $rootOperator = OperatorQuerySearch::getRootOperator($operator['type']); $needsContext = (bool)config(sprintf('search.operators.%s.needs_context', $rootOperator)); diff --git a/app/Support/Http/Controllers/TransactionCalculation.php b/app/Support/Http/Controllers/TransactionCalculation.php index a872b53807..17df264ce8 100644 --- a/app/Support/Http/Controllers/TransactionCalculation.php +++ b/app/Support/Http/Controllers/TransactionCalculation.php @@ -39,14 +39,15 @@ trait TransactionCalculation */ protected function getExpensesForOpposing(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): array { - $total = $accounts->merge($opposing); + $total = $accounts->merge($opposing); /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setAccounts($total) - ->setRange($start, $end) - ->withAccountInformation() - ->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); + ->setRange($start, $end) + ->withAccountInformation() + ->setTypes([TransactionTypeEnum::WITHDRAWAL->value]) + ; return $collector->getExtractedJournals(); } @@ -60,7 +61,8 @@ trait TransactionCalculation $collector = app(GroupCollectorInterface::class); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::TRANSFER->value]) - ->setTags($tags)->withAccountInformation(); + ->setTags($tags)->withAccountInformation() + ; return $collector->getExtractedJournals(); } @@ -73,7 +75,8 @@ trait TransactionCalculation /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::TRANSFER->value]) - ->setBudgets($budgets)->withAccountInformation(); + ->setBudgets($budgets)->withAccountInformation() + ; return $collector->getExtractedJournals(); } @@ -90,7 +93,8 @@ trait TransactionCalculation ->setRange($start, $end) ->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::TRANSFER->value]) ->setCategories($categories) - ->withAccountInformation(); + ->withAccountInformation() + ; return $collector->getExtractedJournals(); } @@ -103,7 +107,8 @@ trait TransactionCalculation /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::TRANSFER->value]) - ->setCategories($categories)->withAccountInformation(); + ->setCategories($categories)->withAccountInformation() + ; return $collector->getExtractedJournals(); } @@ -113,7 +118,7 @@ trait TransactionCalculation */ protected function getIncomeForOpposing(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): array { - $total = $accounts->merge($opposing); + $total = $accounts->merge($opposing); /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); @@ -130,7 +135,8 @@ trait TransactionCalculation /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::TRANSFER->value]) - ->setTags($tags)->withAccountInformation(); + ->setTags($tags)->withAccountInformation() + ; return $collector->getExtractedJournals(); } diff --git a/app/Support/Http/Controllers/UserNavigation.php b/app/Support/Http/Controllers/UserNavigation.php index fc34201443..bee31f429e 100644 --- a/app/Support/Http/Controllers/UserNavigation.php +++ b/app/Support/Http/Controllers/UserNavigation.php @@ -69,7 +69,7 @@ trait UserNavigation final protected function isEditableGroup(TransactionGroup $group): bool { /** @var null|TransactionJournal $journal */ - $journal = $group->transactionJournals()->first(); + $journal = $group->transactionJournals()->first(); if (null === $journal) { return false; } @@ -96,10 +96,10 @@ trait UserNavigation return redirect(route('index')); } - $journal = $transaction->transactionJournal; + $journal = $transaction->transactionJournal; /** @var null|Transaction $other */ - $other = $journal->transactions()->where('id', '!=', $transaction->id)->first(); + $other = $journal->transactions()->where('id', '!=', $transaction->id)->first(); if (null === $other) { app('log')->error(sprintf('Account #%d has no valid journals. Dont know where it belongs.', $account->id)); session()->flash('error', trans('firefly.cant_find_redirect_account')); @@ -119,7 +119,7 @@ trait UserNavigation final protected function redirectGroupToAccount(TransactionGroup $group) { /** @var null|TransactionJournal $journal */ - $journal = $group->transactionJournals()->first(); + $journal = $group->transactionJournals()->first(); if (null === $journal) { app('log')->error(sprintf('No journals in group #%d', $group->id)); diff --git a/app/Support/JsonApi/Enrichments/AccountEnrichment.php b/app/Support/JsonApi/Enrichments/AccountEnrichment.php index 7dc3cb444c..7aedeb6880 100644 --- a/app/Support/JsonApi/Enrichments/AccountEnrichment.php +++ b/app/Support/JsonApi/Enrichments/AccountEnrichment.php @@ -112,7 +112,7 @@ class AccountEnrichment implements EnrichmentInterface } #[Override] - public function enrichSingle(array | Model $model): Account | array + public function enrichSingle(array|Model $model): Account|array { Log::debug(__METHOD__); $collection = new Collection()->push($model); @@ -168,9 +168,9 @@ class AccountEnrichment implements EnrichmentInterface private function appendCollectedData(): void { $this->collection = $this->collection->map(function (Account $item) { - $id = (int)$item->id; - $item->full_account_type = $this->accountTypes[(int)$item->account_type_id] ?? null; - $meta = [ + $id = (int)$item->id; + $item->full_account_type = $this->accountTypes[(int)$item->account_type_id] ?? null; + $meta = [ 'currency' => null, 'location' => [ 'latitude' => null, @@ -217,30 +217,30 @@ class AccountEnrichment implements EnrichmentInterface // add balances // get currencies: - $currency = $this->primaryCurrency; // assume primary currency + $currency = $this->primaryCurrency; // assume primary currency if (null !== $meta['currency']) { $currency = $meta['currency']; } // get the current balance: - $date = $this->getDate(); + $date = $this->getDate(); // $finalBalance = Steam::finalAccountBalance($item, $date, $this->primaryCurrency, $this->convertToPrimary); - $finalBalance = $this->balances[$id]; - $balanceDifference = $this->getBalanceDifference($id, $currency); + $finalBalance = $this->balances[$id]; + $balanceDifference = $this->getBalanceDifference($id, $currency); Log::debug(sprintf('Call finalAccountBalance(%s) with date/time "%s"', var_export($this->convertToPrimary, true), $date->toIso8601String()), $finalBalance); // collect current balances: - $currentBalance = Steam::bcround($finalBalance[$currency->code] ?? '0', $currency->decimal_places); - $openingBalance = Steam::bcround($meta['opening_balance_amount'] ?? '0', $currency->decimal_places); - $virtualBalance = Steam::bcround($item->virtual_balance ?? '0', $currency->decimal_places); - $debtAmount = $meta['current_debt'] ?? null; + $currentBalance = Steam::bcround($finalBalance[$currency->code] ?? '0', $currency->decimal_places); + $openingBalance = Steam::bcround($meta['opening_balance_amount'] ?? '0', $currency->decimal_places); + $virtualBalance = Steam::bcround($item->virtual_balance ?? '0', $currency->decimal_places); + $debtAmount = $meta['current_debt'] ?? null; // set some pc_ default values to NULL: - $pcCurrentBalance = null; - $pcOpeningBalance = null; - $pcVirtualBalance = null; - $pcDebtAmount = null; - $pcBalanceDifference = null; + $pcCurrentBalance = null; + $pcOpeningBalance = null; + $pcVirtualBalance = null; + $pcDebtAmount = null; + $pcBalanceDifference = null; // convert to primary currency if needed: if ($this->convertToPrimary && $currency->id !== $this->primaryCurrency->id) { @@ -279,7 +279,7 @@ class AccountEnrichment implements EnrichmentInterface 'pc_balance_difference' => $pcBalanceDifference, ]; // end add balances - $item->meta = $meta; + $item->meta = $meta; return $item; }); @@ -313,23 +313,25 @@ class AccountEnrichment implements EnrichmentInterface private function collectLocations(): void { $locations = Location::query()->whereIn('locatable_id', $this->ids) - ->where('locatable_type', Account::class)->get(['locations.locatable_id', 'locations.latitude', 'locations.longitude', 'locations.zoom_level'])->toArray(); + ->where('locatable_type', Account::class)->get(['locations.locatable_id', 'locations.latitude', 'locations.longitude', 'locations.zoom_level'])->toArray() + ; foreach ($locations as $location) { $this->locations[(int)$location['locatable_id']] = [ - 'latitude' => (float)$location['latitude'], - 'longitude' => (float)$location['longitude'], - 'zoom_level' => (int)$location['zoom_level'], - ]; + 'latitude' => (float)$location['latitude'], + 'longitude' => (float)$location['longitude'], + 'zoom_level' => (int)$location['zoom_level'], + ]; } Log::debug(sprintf('Enrich with %d locations(s)', count($this->locations))); } private function collectMetaData(): void { - $set = AccountMeta::whereIn('name', ['is_multi_currency', 'include_net_worth', 'currency_id', 'account_role', 'account_number', 'BIC', 'liability_direction', 'interest', 'interest_period', 'current_debt']) - ->whereIn('account_id', $this->ids) - ->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data'])->toArray(); + $set = AccountMeta::whereIn('name', ['is_multi_currency', 'include_net_worth', 'currency_id', 'account_role', 'account_number', 'BIC', 'liability_direction', 'interest', 'interest_period', 'current_debt']) + ->whereIn('account_id', $this->ids) + ->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data'])->toArray() + ; /** @var array $entry */ foreach ($set as $entry) { @@ -355,9 +357,10 @@ class AccountEnrichment implements EnrichmentInterface private function collectNotes(): void { $notes = Note::query()->whereIn('noteable_id', $this->ids) - ->whereNotNull('notes.text') - ->where('notes.text', '!=', '') - ->where('noteable_type', Account::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); + ->whereNotNull('notes.text') + ->where('notes.text', '!=', '') + ->where('noteable_type', Account::class)->get(['notes.noteable_id', 'notes.text'])->toArray() + ; foreach ($notes as $note) { $this->notes[(int)$note['noteable_id']] = (string)$note['text']; } @@ -366,12 +369,13 @@ class AccountEnrichment implements EnrichmentInterface private function collectObjectGroups(): void { - $set = DB::table('object_groupables') - ->whereIn('object_groupable_id', $this->ids) - ->where('object_groupable_type', Account::class) - ->get(['object_groupable_id', 'object_group_id']); + $set = DB::table('object_groupables') + ->whereIn('object_groupable_id', $this->ids) + ->where('object_groupable_type', Account::class) + ->get(['object_groupable_id', 'object_group_id']) + ; - $ids = array_unique($set->pluck('object_group_id')->toArray()); + $ids = array_unique($set->pluck('object_group_id')->toArray()); foreach ($set as $entry) { $this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id; @@ -395,19 +399,20 @@ class AccountEnrichment implements EnrichmentInterface ->setUserGroup($this->userGroup) ->setAccounts($this->collection) ->withAccountInformation() - ->setTypes([TransactionTypeEnum::OPENING_BALANCE->value]); - $journals = $collector->getExtractedJournals(); + ->setTypes([TransactionTypeEnum::OPENING_BALANCE->value]) + ; + $journals = $collector->getExtractedJournals(); foreach ($journals as $journal) { $this->openingBalances[(int)$journal['source_account_id']] = [ - 'amount' => Steam::negative($journal['amount']), - 'date' => $journal['date'], - ]; + 'amount' => Steam::negative($journal['amount']), + 'date' => $journal['date'], + ]; $this->openingBalances[(int)$journal['destination_account_id']] = [ - 'amount' => Steam::positive($journal['amount']), - 'date' => $journal['date'], - ]; + 'amount' => Steam::positive($journal['amount']), + 'date' => $journal['date'], + ]; } } @@ -431,8 +436,8 @@ class AccountEnrichment implements EnrichmentInterface if (0 === count($startBalance) || 0 === count($endBalance)) { return null; } - $start = $startBalance[$currency->code] ?? '0'; - $end = $endBalance[$currency->code] ?? '0'; + $start = $startBalance[$currency->code] ?? '0'; + $end = $endBalance[$currency->code] ?? '0'; return bcsub($end, $start); } @@ -453,7 +458,7 @@ class AccountEnrichment implements EnrichmentInterface case 'current_balance': case 'pc_current_balance': - $this->collection = $this->collection->sortBy(static fn(Account $account) => $account->meta['balances'][$parameter[0]] ?? '0', SORT_NUMERIC, 'desc' === $parameter[1]); + $this->collection = $this->collection->sortBy(static fn (Account $account) => $account->meta['balances'][$parameter[0]] ?? '0', SORT_NUMERIC, 'desc' === $parameter[1]); break; } diff --git a/app/Support/JsonApi/Enrichments/AvailableBudgetEnrichment.php b/app/Support/JsonApi/Enrichments/AvailableBudgetEnrichment.php index 85711c7efb..a6a8823368 100644 --- a/app/Support/JsonApi/Enrichments/AvailableBudgetEnrichment.php +++ b/app/Support/JsonApi/Enrichments/AvailableBudgetEnrichment.php @@ -79,7 +79,7 @@ class AvailableBudgetEnrichment implements EnrichmentInterface } #[Override] - public function enrichSingle(array | Model $model): array | Model + public function enrichSingle(array|Model $model): array|Model { Log::debug(__METHOD__); $collection = new Collection()->push($model); diff --git a/app/Support/JsonApi/Enrichments/BudgetEnrichment.php b/app/Support/JsonApi/Enrichments/BudgetEnrichment.php index 71e4ff160b..cb875aca78 100644 --- a/app/Support/JsonApi/Enrichments/BudgetEnrichment.php +++ b/app/Support/JsonApi/Enrichments/BudgetEnrichment.php @@ -70,7 +70,7 @@ class BudgetEnrichment implements EnrichmentInterface return $this->collection; } - public function enrichSingle(array | Model $model): array | Model + public function enrichSingle(array|Model $model): array|Model { Log::debug(__METHOD__); $collection = new Collection()->push($model); @@ -103,8 +103,8 @@ class BudgetEnrichment implements EnrichmentInterface private function appendCollectedData(): void { $this->collection = $this->collection->map(function (Budget $item) { - $id = (int)$item->id; - $meta = [ + $id = (int)$item->id; + $meta = [ 'object_group_id' => null, 'object_group_order' => null, 'object_group_title' => null, @@ -156,7 +156,7 @@ class BudgetEnrichment implements EnrichmentInterface $opsRepository->setUserGroup($this->userGroup); // $spent = $this->beautify(); // $set = $this->opsRepository->sumExpenses($start, $end, null, new Collection()->push($budget)) - $expenses = $opsRepository->collectExpenses($this->start, $this->end, null, $this->collection, null); + $expenses = $opsRepository->collectExpenses($this->start, $this->end, null, $this->collection, null); foreach ($this->collection as $item) { $id = (int)$item->id; $this->spent[$id] = array_values($opsRepository->sumCollectedExpensesByBudget($expenses, $item, false)); @@ -177,9 +177,10 @@ class BudgetEnrichment implements EnrichmentInterface private function collectNotes(): void { $notes = Note::query()->whereIn('noteable_id', $this->ids) - ->whereNotNull('notes.text') - ->where('notes.text', '!=', '') - ->where('noteable_type', Budget::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); + ->whereNotNull('notes.text') + ->where('notes.text', '!=', '') + ->where('noteable_type', Budget::class)->get(['notes.noteable_id', 'notes.text'])->toArray() + ; foreach ($notes as $note) { $this->notes[(int)$note['noteable_id']] = (string)$note['text']; } @@ -188,12 +189,13 @@ class BudgetEnrichment implements EnrichmentInterface private function collectObjectGroups(): void { - $set = DB::table('object_groupables') - ->whereIn('object_groupable_id', $this->ids) - ->where('object_groupable_type', Budget::class) - ->get(['object_groupable_id', 'object_group_id']); + $set = DB::table('object_groupables') + ->whereIn('object_groupable_id', $this->ids) + ->where('object_groupable_type', Budget::class) + ->get(['object_groupable_id', 'object_group_id']) + ; - $ids = array_unique($set->pluck('object_group_id')->toArray()); + $ids = array_unique($set->pluck('object_group_id')->toArray()); foreach ($set as $entry) { $this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id; diff --git a/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php b/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php index f0a7fd3479..db1e248708 100644 --- a/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php +++ b/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php @@ -73,7 +73,7 @@ class BudgetLimitEnrichment implements EnrichmentInterface return $this->collection; } - public function enrichSingle(array | Model $model): array | Model + public function enrichSingle(array|Model $model): array|Model { Log::debug(__METHOD__); $collection = new Collection()->push($model); @@ -115,12 +115,12 @@ class BudgetLimitEnrichment implements EnrichmentInterface private function collectBudgets(): void { - $budgetIds = $this->collection->pluck('budget_id')->unique()->toArray(); - $budgets = Budget::whereIn('id', $budgetIds)->get(); + $budgetIds = $this->collection->pluck('budget_id')->unique()->toArray(); + $budgets = Budget::whereIn('id', $budgetIds)->get(); $repository = app(OperationsRepository::class); $repository->setUser($this->user); - $expenses = $repository->collectExpenses($this->start, $this->end, null, $budgets, null); + $expenses = $repository->collectExpenses($this->start, $this->end, null, $budgets, null); /** @var BudgetLimit $budgetLimit */ foreach ($this->collection as $budgetLimit) { @@ -151,8 +151,8 @@ class BudgetLimitEnrichment implements EnrichmentInterface private function collectIds(): void { - $this->start = $this->collection->min('start_date') ?? Carbon::now()->startOfMonth(); - $this->end = $this->collection->max('end_date') ?? Carbon::now()->endOfMonth(); + $this->start = $this->collection->min('start_date') ?? Carbon::now()->startOfMonth(); + $this->end = $this->collection->max('end_date') ?? Carbon::now()->endOfMonth(); /** @var BudgetLimit $limit */ foreach ($this->collection as $limit) { @@ -169,9 +169,10 @@ class BudgetLimitEnrichment implements EnrichmentInterface private function collectNotes(): void { $notes = Note::query()->whereIn('noteable_id', $this->ids) - ->whereNotNull('notes.text') - ->where('notes.text', '!=', '') - ->where('noteable_type', BudgetLimit::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); + ->whereNotNull('notes.text') + ->where('notes.text', '!=', '') + ->where('noteable_type', BudgetLimit::class)->get(['notes.noteable_id', 'notes.text'])->toArray() + ; foreach ($notes as $note) { $this->notes[(int)$note['noteable_id']] = (string)$note['text']; } @@ -180,7 +181,7 @@ class BudgetLimitEnrichment implements EnrichmentInterface private function filterToBudget(array $expenses, int $budget): array { - $result = array_filter($expenses, fn(array $item) => (int)$item['budget_id'] === $budget); + $result = array_filter($expenses, fn (array $item) => (int)$item['budget_id'] === $budget); Log::debug(sprintf('filterToBudget for budget #%d, from %d to %d items', $budget, count($expenses), count($result))); return $result; @@ -188,13 +189,13 @@ class BudgetLimitEnrichment implements EnrichmentInterface private function stringifyIds(): void { - $this->expenses = array_map(fn($first) => array_map(function ($second) { + $this->expenses = array_map(fn ($first) => array_map(function ($second) { $second['currency_id'] = (string)($second['currency_id'] ?? 0); return $second; }, $first), $this->expenses); - $this->pcExpenses = array_map(fn($first) => array_map(function ($second) { + $this->pcExpenses = array_map(fn ($first) => array_map(function ($second) { $second['currency_id'] = (string)($second['currency_id'] ?? 0); return $second; diff --git a/app/Support/JsonApi/Enrichments/CategoryEnrichment.php b/app/Support/JsonApi/Enrichments/CategoryEnrichment.php index 074747011b..975227844f 100644 --- a/app/Support/JsonApi/Enrichments/CategoryEnrichment.php +++ b/app/Support/JsonApi/Enrichments/CategoryEnrichment.php @@ -62,7 +62,7 @@ class CategoryEnrichment implements EnrichmentInterface return $collection; } - public function enrichSingle(array | Model $model): array | Model + public function enrichSingle(array|Model $model): array|Model { Log::debug(__METHOD__); $collection = new Collection()->push($model); @@ -123,9 +123,10 @@ class CategoryEnrichment implements EnrichmentInterface private function collectNotes(): void { $notes = Note::query()->whereIn('noteable_id', $this->ids) - ->whereNotNull('notes.text') - ->where('notes.text', '!=', '') - ->where('noteable_type', Category::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); + ->whereNotNull('notes.text') + ->where('notes.text', '!=', '') + ->where('noteable_type', Category::class)->get(['notes.noteable_id', 'notes.text'])->toArray() + ; foreach ($notes as $note) { $this->notes[(int)$note['noteable_id']] = (string)$note['text']; } @@ -139,9 +140,9 @@ class CategoryEnrichment implements EnrichmentInterface $opsRepository = app(OperationsRepositoryInterface::class); $opsRepository->setUser($this->user); $opsRepository->setUserGroup($this->userGroup); - $expenses = $opsRepository->collectExpenses($this->start, $this->end, null, $this->collection); - $income = $opsRepository->collectIncome($this->start, $this->end, null, $this->collection); - $transfers = $opsRepository->collectTransfers($this->start, $this->end, null, $this->collection); + $expenses = $opsRepository->collectExpenses($this->start, $this->end, null, $this->collection); + $income = $opsRepository->collectIncome($this->start, $this->end, null, $this->collection); + $transfers = $opsRepository->collectTransfers($this->start, $this->end, null, $this->collection); foreach ($this->collection as $item) { $id = (int)$item->id; $this->spent[$id] = array_values($opsRepository->sumCollectedTransactionsByCategory($expenses, $item, 'negative', false)); diff --git a/app/Support/JsonApi/Enrichments/EnrichmentInterface.php b/app/Support/JsonApi/Enrichments/EnrichmentInterface.php index e93ddcf283..0ccfb7c060 100644 --- a/app/Support/JsonApi/Enrichments/EnrichmentInterface.php +++ b/app/Support/JsonApi/Enrichments/EnrichmentInterface.php @@ -33,7 +33,7 @@ interface EnrichmentInterface { public function enrich(Collection $collection): Collection; - public function enrichSingle(array | Model $model): array | Model; + public function enrichSingle(array|Model $model): array|Model; public function setUser(User $user): void; diff --git a/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php b/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php index aebdde8f67..e1afbe0042 100644 --- a/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php +++ b/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php @@ -43,13 +43,13 @@ use Illuminate\Support\Facades\Log; class PiggyBankEnrichment implements EnrichmentInterface { - private array $accountIds = []; // @phpstan-ignore-line - private array $accounts = []; // @phpstan-ignore-line - private array $amounts = []; + private array $accountIds = []; // @phpstan-ignore-line + private array $accounts = []; // @phpstan-ignore-line + private array $amounts = []; private Collection $collection; - private array $currencies = []; - private array $currencyIds = []; - private array $ids = []; + private array $currencies = []; + private array $currencyIds = []; + private array $ids = []; // private array $accountCurrencies = []; private array $mappedObjects = []; private array $notes = []; @@ -77,7 +77,7 @@ class PiggyBankEnrichment implements EnrichmentInterface return $this->collection; } - public function enrichSingle(array | Model $model): array | Model + public function enrichSingle(array|Model $model): array|Model { Log::debug(__METHOD__); $collection = new Collection()->push($model); @@ -100,14 +100,14 @@ class PiggyBankEnrichment implements EnrichmentInterface private function appendCollectedData(): void { $this->collection = $this->collection->map(function (PiggyBank $item) { - $id = (int)$item->id; - $currencyId = (int)$item->transaction_currency_id; - $currency = $this->currencies[$currencyId] ?? $this->primaryCurrency; - $targetAmount = null; + $id = (int)$item->id; + $currencyId = (int)$item->transaction_currency_id; + $currency = $this->currencies[$currencyId] ?? $this->primaryCurrency; + $targetAmount = null; if (0 !== bccomp($item->target_amount, '0')) { $targetAmount = $item->target_amount; } - $meta = [ + $meta = [ 'notes' => $this->notes[$id] ?? null, 'currency' => $this->currencies[$currencyId] ?? null, // 'auto_budget' => $this->autoBudgets[$id] ?? null, @@ -136,17 +136,17 @@ class PiggyBankEnrichment implements EnrichmentInterface } // add current amount(s). foreach ($this->amounts[$id] as $accountId => $row) { - $meta['accounts'][] = [ + $meta['accounts'][] = [ 'account_id' => (string)$accountId, 'name' => $this->accounts[$accountId]['name'] ?? '', 'current_amount' => Steam::bcround($row['current_amount'], $currency->decimal_places), 'pc_current_amount' => Steam::bcround($row['pc_current_amount'], $this->primaryCurrency->decimal_places), ]; - $meta['current_amount'] = bcadd($meta['current_amount'], $row['current_amount']); + $meta['current_amount'] = bcadd($meta['current_amount'], $row['current_amount']); // only add pc_current_amount when the pc_current_amount is set $meta['pc_current_amount'] = null === $row['pc_current_amount'] ? null : bcadd($meta['pc_current_amount'], $row['pc_current_amount']); } - $meta['current_amount'] = Steam::bcround($meta['current_amount'], $currency->decimal_places); + $meta['current_amount'] = Steam::bcround($meta['current_amount'], $currency->decimal_places); // only round this number when pc_current_amount is set. $meta['pc_current_amount'] = null === $meta['pc_current_amount'] ? null : Steam::bcround($meta['pc_current_amount'], $this->primaryCurrency->decimal_places); @@ -160,7 +160,7 @@ class PiggyBankEnrichment implements EnrichmentInterface $meta['save_per_month'] = Steam::bcround($this->getSuggestedMonthlyAmount($item->start_date, $item->target_date, $meta['target_amount'], $meta['current_amount']), $currency->decimal_places); $meta['pc_save_per_month'] = Steam::bcround($this->getSuggestedMonthlyAmount($item->start_date, $item->target_date, $meta['pc_target_amount'], $meta['pc_current_amount']), $currency->decimal_places); - $item->meta = $meta; + $item->meta = $meta; return $item; }); @@ -176,7 +176,7 @@ class PiggyBankEnrichment implements EnrichmentInterface $this->ids[] = $id; $this->currencyIds[$id] = (int)$piggy->transaction_currency_id; } - $this->ids = array_unique($this->ids); + $this->ids = array_unique($this->ids); // collect currencies. $currencies = TransactionCurrency::whereIn('id', $this->currencyIds)->get(); @@ -185,10 +185,10 @@ class PiggyBankEnrichment implements EnrichmentInterface } // collect accounts - $set = DB::table('account_piggy_bank')->whereIn('piggy_bank_id', $this->ids)->get(['piggy_bank_id', 'account_id', 'current_amount', 'native_current_amount']); + $set = DB::table('account_piggy_bank')->whereIn('piggy_bank_id', $this->ids)->get(['piggy_bank_id', 'account_id', 'current_amount', 'native_current_amount']); foreach ($set as $item) { - $id = (int)$item->piggy_bank_id; - $accountId = (int)$item->account_id; + $id = (int)$item->piggy_bank_id; + $accountId = (int)$item->account_id; $this->amounts[$id] ??= []; if (!array_key_exists($id, $this->accountIds)) { $this->accountIds[$id] = (int)$item->account_id; @@ -206,7 +206,7 @@ class PiggyBankEnrichment implements EnrichmentInterface } // get account currency preference for ALL. - $set = AccountMeta::whereIn('account_id', array_values($this->accountIds))->where('name', 'currency_id')->get(); + $set = AccountMeta::whereIn('account_id', array_values($this->accountIds))->where('name', 'currency_id')->get(); /** @var AccountMeta $item */ foreach ($set as $item) { @@ -219,7 +219,7 @@ class PiggyBankEnrichment implements EnrichmentInterface } // get account info. - $set = Account::whereIn('id', array_values($this->accountIds))->get(); + $set = Account::whereIn('id', array_values($this->accountIds))->get(); /** @var Account $item */ foreach ($set as $item) { @@ -234,9 +234,10 @@ class PiggyBankEnrichment implements EnrichmentInterface private function collectNotes(): void { $notes = Note::query()->whereIn('noteable_id', $this->ids) - ->whereNotNull('notes.text') - ->where('notes.text', '!=', '') - ->where('noteable_type', PiggyBank::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); + ->whereNotNull('notes.text') + ->where('notes.text', '!=', '') + ->where('noteable_type', PiggyBank::class)->get(['notes.noteable_id', 'notes.text'])->toArray() + ; foreach ($notes as $note) { $this->notes[(int)$note['noteable_id']] = (string)$note['text']; } @@ -245,12 +246,13 @@ class PiggyBankEnrichment implements EnrichmentInterface private function collectObjectGroups(): void { - $set = DB::table('object_groupables') - ->whereIn('object_groupable_id', $this->ids) - ->where('object_groupable_type', PiggyBank::class) - ->get(['object_groupable_id', 'object_group_id']); + $set = DB::table('object_groupables') + ->whereIn('object_groupable_id', $this->ids) + ->where('object_groupable_type', PiggyBank::class) + ->get(['object_groupable_id', 'object_group_id']) + ; - $ids = array_unique($set->pluck('object_group_id')->toArray()); + $ids = array_unique($set->pluck('object_group_id')->toArray()); foreach ($set as $entry) { $this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id; diff --git a/app/Support/JsonApi/Enrichments/PiggyBankEventEnrichment.php b/app/Support/JsonApi/Enrichments/PiggyBankEventEnrichment.php index 8758dfe94d..56a0714466 100644 --- a/app/Support/JsonApi/Enrichments/PiggyBankEventEnrichment.php +++ b/app/Support/JsonApi/Enrichments/PiggyBankEventEnrichment.php @@ -66,7 +66,7 @@ class PiggyBankEventEnrichment implements EnrichmentInterface return $this->collection; } - public function enrichSingle(array | Model $model): array | Model + public function enrichSingle(array|Model $model): array|Model { Log::debug(__METHOD__); $collection = new Collection()->push($model); @@ -89,10 +89,10 @@ class PiggyBankEventEnrichment implements EnrichmentInterface private function appendCollectedData(): void { $this->collection = $this->collection->map(function (PiggyBankEvent $item) { - $id = (int)$item->id; - $piggyId = (int)$item->piggy_bank_id; - $journalId = (int)$item->transaction_journal_id; - $currency = null; + $id = (int)$item->id; + $piggyId = (int)$item->piggy_bank_id; + $journalId = (int)$item->transaction_journal_id; + $currency = null; if (array_key_exists($piggyId, $this->accountIds)) { $accountId = $this->accountIds[$piggyId]; if (array_key_exists($accountId, $this->accountCurrencies)) { @@ -120,7 +120,7 @@ class PiggyBankEventEnrichment implements EnrichmentInterface } $this->ids = array_unique($this->ids); // collect groups with journal info. - $set = TransactionJournal::whereIn('id', $this->journalIds)->get(['id', 'transaction_group_id']); + $set = TransactionJournal::whereIn('id', $this->journalIds)->get(['id', 'transaction_group_id']); /** @var TransactionJournal $item */ foreach ($set as $item) { @@ -128,7 +128,7 @@ class PiggyBankEventEnrichment implements EnrichmentInterface } // collect account info. - $set = DB::table('account_piggy_bank')->whereIn('piggy_bank_id', $this->piggyBankIds)->get(['piggy_bank_id', 'account_id']); + $set = DB::table('account_piggy_bank')->whereIn('piggy_bank_id', $this->piggyBankIds)->get(['piggy_bank_id', 'account_id']); foreach ($set as $item) { $id = (int)$item->piggy_bank_id; if (!array_key_exists($id, $this->accountIds)) { @@ -137,12 +137,12 @@ class PiggyBankEventEnrichment implements EnrichmentInterface } // get account currency preference for ALL. - $set = AccountMeta::whereIn('account_id', array_values($this->accountIds))->where('name', 'currency_id')->get(); + $set = AccountMeta::whereIn('account_id', array_values($this->accountIds))->where('name', 'currency_id')->get(); /** @var AccountMeta $item */ foreach ($set as $item) { - $accountId = (int)$item->account_id; - $currencyId = (int)$item->data; + $accountId = (int)$item->account_id; + $currencyId = (int)$item->data; if (!array_key_exists($currencyId, $this->currencies)) { $this->currencies[$currencyId] = Amount::getTransactionCurrencyById($currencyId); } diff --git a/app/Support/JsonApi/Enrichments/RecurringEnrichment.php b/app/Support/JsonApi/Enrichments/RecurringEnrichment.php index 30484f5388..7fdfc1c5dc 100644 --- a/app/Support/JsonApi/Enrichments/RecurringEnrichment.php +++ b/app/Support/JsonApi/Enrichments/RecurringEnrichment.php @@ -51,11 +51,12 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; + use function Safe\json_decode; class RecurringEnrichment implements EnrichmentInterface { - private array $accounts = []; + private array $accounts = []; private Collection $collection; // private array $transactionTypeIds = []; // private array $transactionTypes = []; @@ -97,7 +98,7 @@ class RecurringEnrichment implements EnrichmentInterface return $this->collection; } - public function enrichSingle(array | Model $model): array | Model + public function enrichSingle(array|Model $model): array|Model { Log::debug(__METHOD__); $collection = new Collection()->push($model); @@ -131,7 +132,7 @@ class RecurringEnrichment implements EnrichmentInterface return (string)trans('firefly.recurring_monthly', ['dayOfMonth' => $repetition->repetition_moment, 'skip' => $repetition->repetition_skip - 1], $this->language); } if ('ndom' === $repetition->repetition_type) { - $parts = explode(',', $repetition->repetition_moment); + $parts = explode(',', $repetition->repetition_moment); // first part is number of week, second is weekday. $dayOfWeek = trans(sprintf('config.dow_%s', $parts[1]), [], $this->language); if ($repetition->repetition_skip > 0) { @@ -148,7 +149,7 @@ class RecurringEnrichment implements EnrichmentInterface } // $diffInYears = (int)$today->diffInYears($repDate, true); // $repDate->addYears($diffInYears); // technically not necessary. - $string = $repDate->isoFormat((string)trans('config.month_and_day_no_year_js')); + $string = $repDate->isoFormat((string)trans('config.month_and_day_no_year_js')); return (string)trans('firefly.recurring_yearly', ['date' => $string], $this->language); } @@ -171,8 +172,8 @@ class RecurringEnrichment implements EnrichmentInterface private function appendCollectedData(): void { $this->collection = $this->collection->map(function (Recurrence $item) { - $id = (int)$item->id; - $meta = [ + $id = (int)$item->id; + $meta = [ 'notes' => $this->notes[$id] ?? null, 'repetitions' => array_values($this->repetitions[$id] ?? []), 'transactions' => $this->processTransactions(array_values($this->transactions[$id] ?? [])), @@ -285,7 +286,7 @@ class RecurringEnrichment implements EnrichmentInterface { /** @var Recurrence $recurrence */ foreach ($this->collection as $recurrence) { - $id = (int)$recurrence->id; + $id = (int)$recurrence->id; // $typeId = (int)$recurrence->transaction_type_id; $this->ids[] = $id; // $this->transactionTypeIds[$id] = $typeId; @@ -303,9 +304,10 @@ class RecurringEnrichment implements EnrichmentInterface private function collectNotes(): void { $notes = Note::query()->whereIn('noteable_id', $this->ids) - ->whereNotNull('notes.text') - ->where('notes.text', '!=', '') - ->where('noteable_type', Recurrence::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); + ->whereNotNull('notes.text') + ->where('notes.text', '!=', '') + ->where('noteable_type', Recurrence::class)->get(['notes.noteable_id', 'notes.text'])->toArray() + ; foreach ($notes as $note) { $this->notes[(int)$note['noteable_id']] = (string)$note['text']; } @@ -335,20 +337,20 @@ class RecurringEnrichment implements EnrichmentInterface Log::debug('Start of enrichment: collectRepetitions()'); $repository = app(RecurringRepositoryInterface::class); $repository->setUserGroup($this->userGroup); - $set = RecurrenceRepetition::whereIn('recurrence_id', $this->ids)->get(); + $set = RecurrenceRepetition::whereIn('recurrence_id', $this->ids)->get(); /** @var RecurrenceRepetition $repetition */ foreach ($set as $repetition) { - $recurrence = $this->collection->filter(fn(Recurrence $item) => (int)$item->id === (int)$repetition->recurrence_id)->first(); - $fromDate = clone($recurrence->latest_date ?? $recurrence->first_date); - $id = (int)$repetition->recurrence_id; - $repId = (int)$repetition->id; + $recurrence = $this->collection->filter(fn (Recurrence $item) => (int)$item->id === (int)$repetition->recurrence_id)->first(); + $fromDate = clone ($recurrence->latest_date ?? $recurrence->first_date); + $id = (int)$repetition->recurrence_id; + $repId = (int)$repetition->id; $this->repetitions[$id] ??= []; // get the (future) occurrences for this specific type of repetition: - $amount = 'daily' === $repetition->repetition_type ? 9 : 5; - $set = $repository->getXOccurrencesSince($repetition, $fromDate, now(config('app.timezone')), $amount); - $occurrences = []; + $amount = 'daily' === $repetition->repetition_type ? 9 : 5; + $set = $repository->getXOccurrencesSince($repetition, $fromDate, now(config('app.timezone')), $amount); + $occurrences = []; /** @var Carbon $carbon */ foreach ($set as $carbon) { @@ -371,8 +373,8 @@ class RecurringEnrichment implements EnrichmentInterface private function collectTransactionMetaData(): void { - $ids = array_keys($this->transactions); - $meta = RecurrenceTransactionMeta::whereNull('deleted_at')->whereIn('rt_id', $ids)->get(); + $ids = array_keys($this->transactions); + $meta = RecurrenceTransactionMeta::whereNull('deleted_at')->whereIn('rt_id', $ids)->get(); // other meta-data to be collected: $billIds = []; $piggyBankIds = []; @@ -384,8 +386,8 @@ class RecurringEnrichment implements EnrichmentInterface $transactionId = (int)$entry->rt_id; // this should refer to another array, were rtIds can be used to find the recurrence. - $recurrenceId = $this->recurrenceIds[$transactionId] ?? 0; - $name = (string)($entry->name ?? ''); + $recurrenceId = $this->recurrenceIds[$transactionId] ?? 0; + $name = (string)($entry->name ?? ''); if (0 === $recurrenceId) { Log::error(sprintf('Could not find recurrence ID for recurrence transaction ID %d', $transactionId)); @@ -485,14 +487,14 @@ class RecurringEnrichment implements EnrichmentInterface /** @var RecurrenceTransaction $transaction */ foreach ($set as $transaction) { - $id = (int)$transaction->recurrence_id; - $transactionId = (int)$transaction->id; - $this->recurrenceIds[$transactionId] = $id; - $this->transactions[$id] ??= []; - $amount = $transaction->amount; - $foreignAmount = $transaction->foreign_amount; + $id = (int)$transaction->recurrence_id; + $transactionId = (int)$transaction->id; + $this->recurrenceIds[$transactionId] = $id; + $this->transactions[$id] ??= []; + $amount = $transaction->amount; + $foreignAmount = $transaction->foreign_amount; - $this->transactions[$id][$transactionId] = [ + $this->transactions[$id][$transactionId] = [ 'id' => (string)$transactionId, // 'recurrence_id' => $id, 'transaction_currency_id' => (int)$transaction->transaction_currency_id, @@ -529,8 +531,8 @@ class RecurringEnrichment implements EnrichmentInterface private function getLanguage(): void { /** @var Preference $preference */ - $preference = Preferences::getForUser($this->user, 'language', config('firefly.default_language', 'en_US')); - $language = $preference->data; + $preference = Preferences::getForUser($this->user, 'language', config('firefly.default_language', 'en_US')); + $language = $preference->data; if (is_array($language)) { $language = 'en_US'; } @@ -543,9 +545,9 @@ class RecurringEnrichment implements EnrichmentInterface $return = []; $converter = new ExchangeRateConverter(); foreach ($transactions as $transaction) { - $currencyId = $transaction['transaction_currency_id']; - $pcAmount = null; - $pcForeignAmount = null; + $currencyId = $transaction['transaction_currency_id']; + $pcAmount = null; + $pcForeignAmount = null; // set the same amount in the primary currency, if both are the same anyway. if (true === $this->convertToPrimary && $currencyId === (int)$this->primaryCurrency->id) { $pcAmount = $transaction['amount']; @@ -561,26 +563,26 @@ class RecurringEnrichment implements EnrichmentInterface } } - $transaction['pc_amount'] = $pcAmount; - $transaction['pc_foreign_amount'] = $pcForeignAmount; + $transaction['pc_amount'] = $pcAmount; + $transaction['pc_foreign_amount'] = $pcForeignAmount; - $sourceId = $transaction['source_id']; - $transaction['source_name'] = $this->accounts[$sourceId]->name; - $transaction['source_iban'] = $this->accounts[$sourceId]->iban; - $transaction['source_type'] = $this->accounts[$sourceId]->accountType->type; - $transaction['source_id'] = (string)$transaction['source_id']; + $sourceId = $transaction['source_id']; + $transaction['source_name'] = $this->accounts[$sourceId]->name; + $transaction['source_iban'] = $this->accounts[$sourceId]->iban; + $transaction['source_type'] = $this->accounts[$sourceId]->accountType->type; + $transaction['source_id'] = (string)$transaction['source_id']; - $destId = $transaction['destination_id']; - $transaction['destination_name'] = $this->accounts[$destId]->name; - $transaction['destination_iban'] = $this->accounts[$destId]->iban; - $transaction['destination_type'] = $this->accounts[$destId]->accountType->type; - $transaction['destination_id'] = (string)$transaction['destination_id']; + $destId = $transaction['destination_id']; + $transaction['destination_name'] = $this->accounts[$destId]->name; + $transaction['destination_iban'] = $this->accounts[$destId]->iban; + $transaction['destination_type'] = $this->accounts[$destId]->accountType->type; + $transaction['destination_id'] = (string)$transaction['destination_id']; - $transaction['currency_id'] = (string)$currencyId; - $transaction['currency_name'] = $this->currencies[$currencyId]->name; - $transaction['currency_code'] = $this->currencies[$currencyId]->code; - $transaction['currency_symbol'] = $this->currencies[$currencyId]->symbol; - $transaction['currency_decimal_places'] = $this->currencies[$currencyId]->decimal_places; + $transaction['currency_id'] = (string)$currencyId; + $transaction['currency_name'] = $this->currencies[$currencyId]->name; + $transaction['currency_code'] = $this->currencies[$currencyId]->code; + $transaction['currency_symbol'] = $this->currencies[$currencyId]->symbol; + $transaction['currency_decimal_places'] = $this->currencies[$currencyId]->decimal_places; $transaction['primary_currency_id'] = (string)$this->primaryCurrency->id; $transaction['primary_currency_name'] = $this->primaryCurrency->name; @@ -602,7 +604,7 @@ class RecurringEnrichment implements EnrichmentInterface $transaction['foreign_currency_decimal_places'] = $this->currencies[$currencyId]->decimal_places; } unset($transaction['transaction_currency_id']); - $return[] = $transaction; + $return[] = $transaction; } return $return; diff --git a/app/Support/JsonApi/Enrichments/SubscriptionEnrichment.php b/app/Support/JsonApi/Enrichments/SubscriptionEnrichment.php index 285e0ad37e..68c3dc0f12 100644 --- a/app/Support/JsonApi/Enrichments/SubscriptionEnrichment.php +++ b/app/Support/JsonApi/Enrichments/SubscriptionEnrichment.php @@ -86,11 +86,11 @@ class SubscriptionEnrichment implements EnrichmentInterface $paidDates = $this->paidDates; $payDates = $this->payDates; $this->collection = $this->collection->map(function (Bill $item) use ($notes, $objectGroups, $paidDates, $payDates) { - $id = (int)$item->id; - $currency = $item->transactionCurrency; - $nem = $this->getNextExpectedMatch($payDates[$id] ?? []); + $id = (int)$item->id; + $currency = $item->transactionCurrency; + $nem = $this->getNextExpectedMatch($payDates[$id] ?? []); - $meta = [ + $meta = [ 'notes' => null, 'object_group_id' => null, 'object_group_title' => null, @@ -101,7 +101,7 @@ class SubscriptionEnrichment implements EnrichmentInterface 'nem' => $nem, 'nem_diff' => $this->getNextExpectedMatchDiff($nem, $payDates[$id] ?? []), ]; - $amounts = [ + $amounts = [ 'amount_min' => Steam::bcround($item->amount_min, $currency->decimal_places), 'amount_max' => Steam::bcround($item->amount_max, $currency->decimal_places), 'average' => Steam::bcround(bcdiv(bcadd($item->amount_min, $item->amount_max), '2'), $currency->decimal_places), @@ -142,7 +142,7 @@ class SubscriptionEnrichment implements EnrichmentInterface return $collection; } - public function enrichSingle(array | Model $model): array | Model + public function enrichSingle(array|Model $model): array|Model { Log::debug(__METHOD__); $collection = new Collection()->push($model); @@ -177,13 +177,13 @@ class SubscriptionEnrichment implements EnrichmentInterface */ protected function lastPaidDate(Bill $subscription, Collection $dates, Carbon $default): Carbon { - $filtered = $dates->filter(fn(TransactionJournal $journal) => (int)$journal->bill_id === (int)$subscription->id); + $filtered = $dates->filter(fn (TransactionJournal $journal) => (int)$journal->bill_id === (int)$subscription->id); Log::debug(sprintf('Filtered down from %d to %d entries for bill #%d.', $dates->count(), $filtered->count(), $subscription->id)); if (0 === $filtered->count()) { return $default; } - $latest = $filtered->first()->date; + $latest = $filtered->first()->date; /** @var TransactionJournal $journal */ foreach ($filtered as $journal) { @@ -198,9 +198,10 @@ class SubscriptionEnrichment implements EnrichmentInterface private function collectNotes(): void { $notes = Note::query()->whereIn('noteable_id', $this->subscriptionIds) - ->whereNotNull('notes.text') - ->where('notes.text', '!=', '') - ->where('noteable_type', Bill::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); + ->whereNotNull('notes.text') + ->where('notes.text', '!=', '') + ->where('noteable_type', Bill::class)->get(['notes.noteable_id', 'notes.text'])->toArray() + ; foreach ($notes as $note) { $this->notes[(int)$note['noteable_id']] = (string)$note['text']; } @@ -209,12 +210,13 @@ class SubscriptionEnrichment implements EnrichmentInterface private function collectObjectGroups(): void { - $set = DB::table('object_groupables') - ->whereIn('object_groupable_id', $this->subscriptionIds) - ->where('object_groupable_type', Bill::class) - ->get(['object_groupable_id', 'object_group_id']); + $set = DB::table('object_groupables') + ->whereIn('object_groupable_id', $this->subscriptionIds) + ->where('object_groupable_type', Bill::class) + ->get(['object_groupable_id', 'object_group_id']) + ; - $ids = array_unique($set->pluck('object_group_id')->toArray()); + $ids = array_unique($set->pluck('object_group_id')->toArray()); foreach ($set as $entry) { $this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id; @@ -242,13 +244,13 @@ class SubscriptionEnrichment implements EnrichmentInterface // 2023-07-18 this particular date is used to search for the last paid date. // 2023-07-18 the cloned $searchDate is used to grab the correct transactions. /** @var Carbon $start */ - $start = clone $this->start; - $searchStart = clone $start; + $start = clone $this->start; + $searchStart = clone $start; $start->subDay(); /** @var Carbon $end */ - $end = clone $this->end; - $searchEnd = clone $end; + $end = clone $this->end; + $searchEnd = clone $end; // move the search dates to the start of the day. $searchStart->startOfDay(); @@ -257,13 +259,13 @@ class SubscriptionEnrichment implements EnrichmentInterface Log::debug(sprintf('Search parameters are: start: %s, end: %s', $searchStart->format('Y-m-d H:i:s'), $searchEnd->format('Y-m-d H:i:s'))); // Get from database when bills were paid. - $set = $this->user->transactionJournals() - ->whereIn('bill_id', $this->subscriptionIds) - ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->leftJoin('transaction_currencies AS currency', 'currency.id', '=', 'transactions.transaction_currency_id') - ->leftJoin('transaction_currencies AS foreign_currency', 'foreign_currency.id', '=', 'transactions.foreign_currency_id') - ->where('transactions.amount', '>', 0) - ->before($searchEnd)->after($searchStart)->get( + $set = $this->user->transactionJournals() + ->whereIn('bill_id', $this->subscriptionIds) + ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->leftJoin('transaction_currencies AS currency', 'currency.id', '=', 'transactions.transaction_currency_id') + ->leftJoin('transaction_currencies AS foreign_currency', 'foreign_currency.id', '=', 'transactions.foreign_currency_id') + ->where('transactions.amount', '>', 0) + ->before($searchEnd)->after($searchStart)->get( [ 'transaction_journals.id', 'transaction_journals.date', @@ -280,24 +282,25 @@ class SubscriptionEnrichment implements EnrichmentInterface 'transactions.amount', 'transactions.foreign_amount', ] - ); + ) + ; Log::debug(sprintf('Count %d entries in set', $set->count())); // for each bill, do a loop. - $converter = new ExchangeRateConverter(); + $converter = new ExchangeRateConverter(); /** @var Bill $subscription */ foreach ($this->collection as $subscription) { // Grab from array the most recent payment. If none exist, fall back to the start date and pretend *that* was the last paid date. Log::debug(sprintf('Grab last paid date from function, return %s if it comes up with nothing.', $start->format('Y-m-d'))); - $lastPaidDate = $this->lastPaidDate($subscription, $set, $start); + $lastPaidDate = $this->lastPaidDate($subscription, $set, $start); Log::debug(sprintf('Result of lastPaidDate is %s', $lastPaidDate->format('Y-m-d'))); // At this point the "next match" is exactly after the last time the bill was paid. - $result = []; - $filtered = $set->filter(fn(TransactionJournal $journal) => (int)$journal->bill_id === (int)$subscription->id); + $result = []; + $filtered = $set->filter(fn (TransactionJournal $journal) => (int)$journal->bill_id === (int)$subscription->id); foreach ($filtered as $entry) { - $array = [ + $array = [ 'transaction_group_id' => (string)$entry->transaction_group_id, 'transaction_journal_id' => (string)$entry->id, 'date' => $entry->date->toAtomString(), @@ -360,12 +363,12 @@ class SubscriptionEnrichment implements EnrichmentInterface /** @var Bill $subscription */ foreach ($this->collection as $subscription) { - $id = (int)$subscription->id; - $lastPaidDate = $this->getLastPaidDate($this->paidDates[$id] ?? []); - $payDates = $this->calculator->getPayDates($this->start, $this->end, $subscription->date, $subscription->repeat_freq, $subscription->skip, $lastPaidDate); - $payDatesFormatted = []; + $id = (int)$subscription->id; + $lastPaidDate = $this->getLastPaidDate($this->paidDates[$id] ?? []); + $payDates = $this->calculator->getPayDates($this->start, $this->end, $subscription->date, $subscription->repeat_freq, $subscription->skip, $lastPaidDate); + $payDatesFormatted = []; foreach ($payDates as $string) { - $date = Carbon::createFromFormat('!Y-m-d', $string, config('app.timezone')); + $date = Carbon::createFromFormat('!Y-m-d', $string, config('app.timezone')); if (!$date instanceof Carbon) { $date = today(config('app.timezone')); } diff --git a/app/Support/JsonApi/Enrichments/TransactionGroupEnrichment.php b/app/Support/JsonApi/Enrichments/TransactionGroupEnrichment.php index 014aff8e68..a8dccfe9fc 100644 --- a/app/Support/JsonApi/Enrichments/TransactionGroupEnrichment.php +++ b/app/Support/JsonApi/Enrichments/TransactionGroupEnrichment.php @@ -83,7 +83,7 @@ class TransactionGroupEnrichment implements EnrichmentInterface } #[Override] - public function enrichSingle(array | Model $model): array | TransactionGroup + public function enrichSingle(array|Model $model): array|TransactionGroup { Log::debug(__METHOD__); if (is_array($model)) { @@ -109,28 +109,28 @@ class TransactionGroupEnrichment implements EnrichmentInterface private function appendCollectedData(): void { - $notes = $this->notes; - $tags = $this->tags; - $metaData = $this->metaData; - $locations = $this->locations; - $attachmentCount = $this->attachmentCount; - $primaryCurrency = $this->primaryCurrency; + $notes = $this->notes; + $tags = $this->tags; + $metaData = $this->metaData; + $locations = $this->locations; + $attachmentCount = $this->attachmentCount; + $primaryCurrency = $this->primaryCurrency; $this->collection = $this->collection->map(function (array $item) use ($primaryCurrency, $notes, $tags, $metaData, $locations, $attachmentCount) { foreach ($item['transactions'] as $index => $transaction) { - $journalId = (int)$transaction['transaction_journal_id']; + $journalId = (int)$transaction['transaction_journal_id']; // attach notes if they exist: - $item['transactions'][$index]['notes'] = array_key_exists($journalId, $notes) ? $notes[$journalId] : null; + $item['transactions'][$index]['notes'] = array_key_exists($journalId, $notes) ? $notes[$journalId] : null; // attach tags if they exist: - $item['transactions'][$index]['tags'] = array_key_exists($journalId, $tags) ? $tags[$journalId] : []; + $item['transactions'][$index]['tags'] = array_key_exists($journalId, $tags) ? $tags[$journalId] : []; // attachment count $item['transactions'][$index]['attachment_count'] = array_key_exists($journalId, $attachmentCount) ? $attachmentCount[$journalId] : 0; // default location data - $item['transactions'][$index]['location'] = [ + $item['transactions'][$index]['location'] = [ 'latitude' => null, 'longitude' => null, 'zoom_level' => null, @@ -146,8 +146,8 @@ class TransactionGroupEnrichment implements EnrichmentInterface ]; // append meta data - $item['transactions'][$index]['meta'] = []; - $item['transactions'][$index]['meta_date'] = []; + $item['transactions'][$index]['meta'] = []; + $item['transactions'][$index]['meta_date'] = []; if (array_key_exists($journalId, $metaData)) { // loop al meta data: foreach ($metaData[$journalId] as $name => $value) { @@ -175,11 +175,12 @@ class TransactionGroupEnrichment implements EnrichmentInterface // select count(id) as nr_of_attachments, attachable_id from attachments // group by attachable_id $attachments = Attachment::query() - ->whereIn('attachable_id', $this->journalIds) - ->where('attachable_type', TransactionJournal::class) - ->groupBy('attachable_id') - ->get(['attachable_id', DB::raw('COUNT(id) as nr_of_attachments')]) - ->toArray(); + ->whereIn('attachable_id', $this->journalIds) + ->where('attachable_type', TransactionJournal::class) + ->groupBy('attachable_id') + ->get(['attachable_id', DB::raw('COUNT(id) as nr_of_attachments')]) + ->toArray() + ; foreach ($attachments as $row) { $this->attachmentCount[(int)$row['attachable_id']] = (int)$row['nr_of_attachments']; } @@ -199,14 +200,15 @@ class TransactionGroupEnrichment implements EnrichmentInterface private function collectLocations(): void { $locations = Location::query()->whereIn('locatable_id', $this->journalIds) - ->where('locatable_type', TransactionJournal::class)->get(['locations.locatable_id', 'locations.latitude', 'locations.longitude', 'locations.zoom_level'])->toArray(); + ->where('locatable_type', TransactionJournal::class)->get(['locations.locatable_id', 'locations.latitude', 'locations.longitude', 'locations.zoom_level'])->toArray() + ; foreach ($locations as $location) { $this->locations[(int)$location['locatable_id']] = [ - 'latitude' => (float)$location['latitude'], - 'longitude' => (float)$location['longitude'], - 'zoom_level' => (int)$location['zoom_level'], - ]; + 'latitude' => (float)$location['latitude'], + 'longitude' => (float)$location['longitude'], + 'zoom_level' => (int)$location['zoom_level'], + ]; } Log::debug(sprintf('Enrich with %d locations(s)', count($this->locations))); } @@ -215,8 +217,8 @@ class TransactionGroupEnrichment implements EnrichmentInterface { $set = TransactionJournalMeta::whereIn('transaction_journal_id', $this->journalIds)->get(['transaction_journal_id', 'name', 'data'])->toArray(); foreach ($set as $entry) { - $name = $entry['name']; - $data = (string)$entry['data']; + $name = $entry['name']; + $data = (string)$entry['data']; if ('' === $data) { continue; } @@ -234,9 +236,10 @@ class TransactionGroupEnrichment implements EnrichmentInterface private function collectNotes(): void { $notes = Note::query()->whereIn('noteable_id', $this->journalIds) - ->whereNotNull('notes.text') - ->where('notes.text', '!=', '') - ->where('noteable_type', TransactionJournal::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); + ->whereNotNull('notes.text') + ->where('notes.text', '!=', '') + ->where('noteable_type', TransactionJournal::class)->get(['notes.noteable_id', 'notes.text'])->toArray() + ; foreach ($notes as $note) { $this->notes[(int)$note['noteable_id']] = (string)$note['text']; } @@ -246,11 +249,12 @@ class TransactionGroupEnrichment implements EnrichmentInterface private function collectTags(): void { $set = Tag::leftJoin('tag_transaction_journal', 'tags.id', '=', 'tag_transaction_journal.tag_id') - ->whereIn('tag_transaction_journal.transaction_journal_id', $this->journalIds) - ->get(['tag_transaction_journal.transaction_journal_id', 'tags.tag'])->toArray(); + ->whereIn('tag_transaction_journal.transaction_journal_id', $this->journalIds) + ->get(['tag_transaction_journal.transaction_journal_id', 'tags.tag'])->toArray() + ; foreach ($set as $item) { $journalId = $item['transaction_journal_id']; - $this->tags[$journalId] ??= []; + $this->tags[$journalId] ??= []; $this->tags[$journalId][] = $item['tag']; } } diff --git a/app/Support/JsonApi/Enrichments/WebhookEnrichment.php b/app/Support/JsonApi/Enrichments/WebhookEnrichment.php index 0004c291c8..e847a16b0f 100644 --- a/app/Support/JsonApi/Enrichments/WebhookEnrichment.php +++ b/app/Support/JsonApi/Enrichments/WebhookEnrichment.php @@ -66,7 +66,7 @@ class WebhookEnrichment implements EnrichmentInterface return $this->collection; } - public function enrichSingle(array | Model $model): array | Model + public function enrichSingle(array|Model $model): array|Model { Log::debug(__METHOD__); $collection = new Collection()->push($model); diff --git a/app/Support/Models/AccountBalanceCalculator.php b/app/Support/Models/AccountBalanceCalculator.php index d9e616f9b9..5675f92adc 100644 --- a/app/Support/Models/AccountBalanceCalculator.php +++ b/app/Support/Models/AccountBalanceCalculator.php @@ -65,9 +65,9 @@ class AccountBalanceCalculator public static function recalculateForJournal(TransactionJournal $transactionJournal): void { Log::debug(__METHOD__); - $object = new self(); + $object = new self(); - $set = []; + $set = []; foreach ($transactionJournal->transactions as $transaction) { $set[$transaction->account_id] = $transaction->account; } @@ -81,17 +81,18 @@ class AccountBalanceCalculator return '0'; } Log::debug(sprintf('getLatestBalance: notBefore date is "%s", calculating', $notBefore->format('Y-m-d'))); - $query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->whereNull('transactions.deleted_at') - ->where('transaction_journals.transaction_currency_id', $currencyId) - ->whereNull('transaction_journals.deleted_at') + $query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->whereNull('transactions.deleted_at') + ->where('transaction_journals.transaction_currency_id', $currencyId) + ->whereNull('transaction_journals.deleted_at') // this order is the same as GroupCollector - ->orderBy('transaction_journals.date', 'DESC') - ->orderBy('transaction_journals.order', 'ASC') - ->orderBy('transaction_journals.id', 'DESC') - ->orderBy('transaction_journals.description', 'DESC') - ->orderBy('transactions.amount', 'DESC') - ->where('transactions.account_id', $accountId); + ->orderBy('transaction_journals.date', 'DESC') + ->orderBy('transaction_journals.order', 'ASC') + ->orderBy('transaction_journals.id', 'DESC') + ->orderBy('transaction_journals.description', 'DESC') + ->orderBy('transactions.amount', 'DESC') + ->where('transactions.account_id', $accountId) + ; $notBefore->startOfDay(); $query->where('transaction_journals.date', '<', $notBefore); @@ -112,14 +113,15 @@ class AccountBalanceCalculator $balances = []; $count = 0; $query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->whereNull('transactions.deleted_at') - ->whereNull('transaction_journals.deleted_at') + ->whereNull('transactions.deleted_at') + ->whereNull('transaction_journals.deleted_at') // this order is the same as GroupCollector, but in the exact reverse. - ->orderBy('transaction_journals.date', 'asc') - ->orderBy('transaction_journals.order', 'desc') - ->orderBy('transaction_journals.id', 'asc') - ->orderBy('transaction_journals.description', 'asc') - ->orderBy('transactions.amount', 'asc'); + ->orderBy('transaction_journals.date', 'asc') + ->orderBy('transaction_journals.order', 'desc') + ->orderBy('transaction_journals.id', 'asc') + ->orderBy('transaction_journals.description', 'asc') + ->orderBy('transactions.amount', 'asc') + ; if ($accounts->count() > 0) { $query->whereIn('transactions.account_id', $accounts->pluck('id')->toArray()); } @@ -128,7 +130,7 @@ class AccountBalanceCalculator $query->where('transaction_journals.date', '>=', $notBefore); } - $set = $query->get(['transactions.id', 'transactions.balance_dirty', 'transactions.transaction_currency_id', 'transaction_journals.date', 'transactions.account_id', 'transactions.amount']); + $set = $query->get(['transactions.id', 'transactions.balance_dirty', 'transactions.transaction_currency_id', 'transaction_journals.date', 'transactions.account_id', 'transactions.amount']); Log::debug(sprintf('Counted %d transaction(s)', $set->count())); // the balance value is an array. @@ -141,8 +143,8 @@ class AccountBalanceCalculator $balances[$entry->account_id][$entry->transaction_currency_id] ??= [$this->getLatestBalance($entry->account_id, $entry->transaction_currency_id, $notBefore), null]; // before and after are easy: - $before = $balances[$entry->account_id][$entry->transaction_currency_id][0]; - $after = bcadd($before, (string)$entry->amount); + $before = $balances[$entry->account_id][$entry->transaction_currency_id][0]; + $after = bcadd($before, (string)$entry->amount); if (true === $entry->balance_dirty || $accounts->count() > 0) { // update the transaction: $entry->balance_before = $before; diff --git a/app/Support/Models/BillDateCalculator.php b/app/Support/Models/BillDateCalculator.php index 9f40348777..3d49c867a3 100644 --- a/app/Support/Models/BillDateCalculator.php +++ b/app/Support/Models/BillDateCalculator.php @@ -49,15 +49,15 @@ class BillDateCalculator Log::debug(sprintf('Dates must be between %s and %s.', $earliest->format('Y-m-d'), $latest->format('Y-m-d'))); Log::debug(sprintf('Bill started on %s, period is "%s", skip is %d, last paid = "%s".', $billStart->format('Y-m-d'), $period, $skip, $lastPaid?->format('Y-m-d'))); - $daysUntilEOM = app('navigation')->daysUntilEndOfMonth($billStart); + $daysUntilEOM = app('navigation')->daysUntilEndOfMonth($billStart); Log::debug(sprintf('For bill start, days until end of month is %d', $daysUntilEOM)); - $set = new Collection(); - $currentStart = clone $earliest; + $set = new Collection(); + $currentStart = clone $earliest; // 2023-06-23 subDay to fix 7655 $currentStart->subDay(); - $loop = 0; + $loop = 0; Log::debug('Start of loop'); while ($currentStart <= $latest) { @@ -107,7 +107,7 @@ class BillDateCalculator // for the next loop, go to end of period, THEN add day. Log::debug('Add one day to nextExpectedMatch/currentStart.'); $nextExpectedMatch->addDay(); - $currentStart = clone $nextExpectedMatch; + $currentStart = clone $nextExpectedMatch; ++$loop; if ($loop > 31) { @@ -117,8 +117,8 @@ class BillDateCalculator } } Log::debug('end of loop'); - $simple = $set->map( // @phpstan-ignore-line - static fn(Carbon $date) => $date->format('Y-m-d') + $simple = $set->map( // @phpstan-ignore-line + static fn (Carbon $date) => $date->format('Y-m-d') ); Log::debug(sprintf('Found %d pay dates', $set->count()), $simple->toArray()); @@ -140,7 +140,7 @@ class BillDateCalculator return $billStartDate; } - $steps = app('navigation')->diffInPeriods($period, $skip, $earliest, $billStartDate); + $steps = app('navigation')->diffInPeriods($period, $skip, $earliest, $billStartDate); if ($steps === $this->diffInMonths) { Log::debug(sprintf('Steps is %d, which is the same as diffInMonths (%d), so we add another 1.', $steps, $this->diffInMonths)); ++$steps; diff --git a/app/Support/Models/ReturnsIntegerIdTrait.php b/app/Support/Models/ReturnsIntegerIdTrait.php index d8178e07a5..f3fac096ce 100644 --- a/app/Support/Models/ReturnsIntegerIdTrait.php +++ b/app/Support/Models/ReturnsIntegerIdTrait.php @@ -39,7 +39,7 @@ trait ReturnsIntegerIdTrait protected function id(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Support/Models/ReturnsIntegerUserIdTrait.php b/app/Support/Models/ReturnsIntegerUserIdTrait.php index 8eca6e943c..3f1808923d 100644 --- a/app/Support/Models/ReturnsIntegerUserIdTrait.php +++ b/app/Support/Models/ReturnsIntegerUserIdTrait.php @@ -37,14 +37,14 @@ trait ReturnsIntegerUserIdTrait protected function userGroupId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } protected function userId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php index a1e2c256c7..cb9ac29d9f 100644 --- a/app/Support/Navigation.php +++ b/app/Support/Navigation.php @@ -77,10 +77,10 @@ class Navigation if (!array_key_exists($repeatFreq, $functionMap)) { Log::error(sprintf( - 'The periodicity %s is unknown. Choose one of available periodicity: %s', - $repeatFreq, - implode(', ', array_keys($functionMap)) - )); + 'The periodicity %s is unknown. Choose one of available periodicity: %s', + $repeatFreq, + implode(', ', array_keys($functionMap)) + )); return $theDate; } @@ -93,7 +93,7 @@ class Navigation if ($end < $start) { [$start, $end] = [$end, $start]; } - $periods = []; + $periods = []; // first, 13 periods of [range] $loopCount = 0; $loopDate = clone $end; @@ -151,13 +151,13 @@ class Navigation public function diffInPeriods(string $period, int $skip, Carbon $beginning, Carbon $end): int { Log::debug(sprintf( - 'diffInPeriods: %s (skip: %d), between %s and %s.', - $period, - $skip, - $beginning->format('Y-m-d'), - $end->format('Y-m-d') - )); - $map = [ + 'diffInPeriods: %s (skip: %d), between %s and %s.', + $period, + $skip, + $beginning->format('Y-m-d'), + $end->format('Y-m-d') + )); + $map = [ 'daily' => 'diffInDays', 'weekly' => 'diffInWeeks', 'monthly' => 'diffInMonths', @@ -170,7 +170,7 @@ class Navigation return 1; } - $func = $map[$period]; + $func = $map[$period]; // first do the diff $floatDiff = $beginning->{$func}($end, true); // @phpstan-ignore-line @@ -185,7 +185,7 @@ class Navigation } // then do ceil() - $diff = ceil($floatDiff); + $diff = ceil($floatDiff); Log::debug(sprintf('Diff is %f periods (%d rounded up)', $floatDiff, $diff)); @@ -193,11 +193,11 @@ class Navigation $parameter = $skip + 1; $diff = ceil($diff / $parameter) * $parameter; Log::debug(sprintf( - 'diffInPeriods: skip is %d, so param is %d, and diff becomes %d', - $skip, - $parameter, - $diff - )); + 'diffInPeriods: skip is %d, so param is %d, and diff becomes %d', + $skip, + $parameter, + $diff + )); } return (int)$diff; @@ -205,7 +205,7 @@ class Navigation public function endOfPeriod(Carbon $end, string $repeatFreq): Carbon { - $currentEnd = clone $end; + $currentEnd = clone $end; // Log::debug(sprintf('Now in endOfPeriod("%s", "%s").', $currentEnd->toIso8601String(), $repeatFreq)); $functionMap = [ @@ -239,7 +239,7 @@ class Navigation Log::debug('Session data available.'); /** @var Carbon $tStart */ - $tStart = session('start', today(config('app.timezone'))->startOfMonth()); + $tStart = session('start', today(config('app.timezone'))->startOfMonth()); /** @var Carbon $tEnd */ $tEnd = session('end', today(config('app.timezone'))->endOfMonth()); @@ -259,7 +259,7 @@ class Navigation return $end->endOfMonth(); } - $result = match ($repeatFreq) { + $result = match ($repeatFreq) { 'last7' => $currentEnd->addDays(7)->startOfDay(), 'last30' => $currentEnd->addDays(30)->startOfDay(), 'last90' => $currentEnd->addDays(90)->startOfDay(), @@ -279,7 +279,7 @@ class Navigation return $end; } - $function = $functionMap[$repeatFreq]; + $function = $functionMap[$repeatFreq]; if (array_key_exists($repeatFreq, $modifierMap)) { $currentEnd->{$function}($modifierMap[$repeatFreq])->milli(0); // @phpstan-ignore-line @@ -319,7 +319,7 @@ class Navigation 'yearly' => 'endOfYear', ]; - $currentEnd = clone $theCurrentEnd; + $currentEnd = clone $theCurrentEnd; if (array_key_exists($repeatFreq, $functionMap)) { $function = $functionMap[$repeatFreq]; @@ -362,7 +362,7 @@ class Navigation */ public function listOfPeriods(Carbon $start, Carbon $end): array { - $locale = app('steam')->getLocale(); + $locale = app('steam')->getLocale(); // define period to increment $increment = 'addDay'; $format = $this->preferredCarbonFormat($start, $end); @@ -379,8 +379,8 @@ class Navigation $increment = 'addYear'; $displayFormat = (string)trans('config.year_js'); } - $begin = clone $start; - $entries = []; + $begin = clone $start; + $entries = []; while ($begin < $end) { $formatted = $begin->format($format); $displayed = $begin->isoFormat($displayFormat); @@ -439,6 +439,7 @@ class Navigation // special formatter for quarter of year Log::error(sprintf('No date formats for frequency "%s"!', $repeatFrequency)); + throw new FireflyException(sprintf('No date formats for frequency "%s"!', $repeatFrequency)); return $date->format('Y-m-d'); @@ -557,9 +558,9 @@ class Navigation public function startOfPeriod(Carbon $theDate, string $repeatFreq): Carbon { - $date = clone $theDate; + $date = clone $theDate; // Log::debug(sprintf('Now in startOfPeriod("%s", "%s")', $date->toIso8601String(), $repeatFreq)); - $functionMap = [ + $functionMap = [ '1D' => 'startOfDay', 'daily' => 'startOfDay', '1W' => 'startOfWeek', @@ -606,7 +607,7 @@ class Navigation return $date; } - $result = match ($repeatFreq) { + $result = match ($repeatFreq) { 'last7' => $date->subDays(7)->startOfDay(), 'last30' => $date->subDays(30)->startOfDay(), 'last90' => $date->subDays(90)->startOfDay(), @@ -638,7 +639,7 @@ class Navigation public function subtractPeriod(Carbon $theDate, string $repeatFreq, ?int $subtract = null): Carbon { $subtract ??= 1; - $date = clone $theDate; + $date = clone $theDate; // 1D 1W 1M 3M 6M 1Y $functionMap = [ '1D' => 'subDays', @@ -677,7 +678,7 @@ class Navigation // this is then subtracted from $theDate (* $subtract). if ('custom' === $repeatFreq) { /** @var Carbon $tStart */ - $tStart = session('start', today(config('app.timezone'))->startOfMonth()); + $tStart = session('start', today(config('app.timezone'))->startOfMonth()); /** @var Carbon $tEnd */ $tEnd = session('end', today(config('app.timezone'))->endOfMonth()); @@ -771,7 +772,7 @@ class Navigation return $fiscalHelper->endOfFiscalYear($end); } - $list = [ + $list = [ 'last7', 'last30', 'last90', diff --git a/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php b/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php index d71872971c..82f513a44e 100644 --- a/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php +++ b/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php @@ -41,10 +41,10 @@ trait RecalculatesAvailableBudgetsTrait { private function calculateAmount(AvailableBudget $availableBudget): void { - $repository = app(BudgetLimitRepositoryInterface::class); + $repository = app(BudgetLimitRepositoryInterface::class); $repository->setUser($availableBudget->user); - $newAmount = '0'; - $abPeriod = Period::make($availableBudget->start_date, $availableBudget->end_date, Precision::DAY()); + $newAmount = '0'; + $abPeriod = Period::make($availableBudget->start_date, $availableBudget->end_date, Precision::DAY()); Log::debug( sprintf( 'Now at AB #%d, ("%s" to "%s")', @@ -54,7 +54,7 @@ trait RecalculatesAvailableBudgetsTrait ) ); // have to recalculate everything just in case. - $set = $repository->getAllBudgetLimitsByCurrency($availableBudget->transactionCurrency, $availableBudget->start_date, $availableBudget->end_date); + $set = $repository->getAllBudgetLimitsByCurrency($availableBudget->transactionCurrency, $availableBudget->start_date, $availableBudget->end_date); Log::debug(sprintf('Found %d interesting budget limit(s).', $set->count())); /** @var BudgetLimit $budgetLimit */ @@ -69,8 +69,8 @@ trait RecalculatesAvailableBudgetsTrait ); // overlap in days: $limitPeriod = Period::make( - $budgetLimit->start_date, - $budgetLimit->end_date, + $budgetLimit->start_date, + $budgetLimit->end_date, precision : Precision::DAY(), boundaries: Boundaries::EXCLUDE_NONE() ); @@ -111,8 +111,8 @@ trait RecalculatesAvailableBudgetsTrait return '0'; } $limitPeriod = Period::make( - $budgetLimit->start_date, - $budgetLimit->end_date, + $budgetLimit->start_date, + $budgetLimit->end_date, precision : Precision::DAY(), boundaries: Boundaries::EXCLUDE_NONE() ); @@ -130,7 +130,7 @@ trait RecalculatesAvailableBudgetsTrait Log::debug(sprintf('Now in updateAvailableBudget(limit #%d)', $budgetLimit->id)); /** @var null|Budget $budget */ - $budget = Budget::find($budgetLimit->budget_id); + $budget = Budget::find($budgetLimit->budget_id); if (null === $budget) { Log::warning('Budget is null, probably deleted, find deleted version.'); @@ -145,7 +145,7 @@ trait RecalculatesAvailableBudgetsTrait } /** @var null|User $user */ - $user = $budget->user; + $user = $budget->user; // sanity check. It happens when the budget has been deleted so the original user is unknown. if (null === $user) { @@ -161,7 +161,7 @@ trait RecalculatesAvailableBudgetsTrait // all have to be created or updated. try { $viewRange = app('preferences')->getForUser($user, 'viewRange', '1M')->data; - } catch (ContainerExceptionInterface | NotFoundExceptionInterface $e) { + } catch (ContainerExceptionInterface|NotFoundExceptionInterface $e) { Log::error($e->getMessage()); $viewRange = '1M'; } @@ -169,20 +169,20 @@ trait RecalculatesAvailableBudgetsTrait if (null === $viewRange || is_array($viewRange)) { $viewRange = '1M'; } - $viewRange = (string)$viewRange; + $viewRange = (string)$viewRange; - $start = app('navigation')->startOfPeriod($budgetLimit->start_date, $viewRange); - $end = app('navigation')->startOfPeriod($budgetLimit->end_date, $viewRange); - $end = app('navigation')->endOfPeriod($end, $viewRange); + $start = app('navigation')->startOfPeriod($budgetLimit->start_date, $viewRange); + $end = app('navigation')->startOfPeriod($budgetLimit->end_date, $viewRange); + $end = app('navigation')->endOfPeriod($end, $viewRange); // limit period in total is: $limitPeriod = Period::make($start, $end, precision: Precision::DAY(), boundaries: Boundaries::EXCLUDE_NONE()); Log::debug(sprintf('Limit period is from %s to %s', $start->format('Y-m-d'), $end->format('Y-m-d'))); // from the start until the end of the budget limit, need to loop! - $current = clone $start; + $current = clone $start; while ($current <= $end) { - $currentEnd = app('navigation')->endOfPeriod($current, $viewRange); + $currentEnd = app('navigation')->endOfPeriod($current, $viewRange); // create or find AB for this particular period, and set the amount accordingly. /** @var null|AvailableBudget $availableBudget */ @@ -227,7 +227,7 @@ trait RecalculatesAvailableBudgetsTrait } // prep for next loop - $current = app('navigation')->addPeriod($current, $viewRange, 0); + $current = app('navigation')->addPeriod($current, $viewRange, 0); } } } diff --git a/app/Support/ParseDateString.php b/app/Support/ParseDateString.php index bd91585f8b..df60be3d1d 100644 --- a/app/Support/ParseDateString.php +++ b/app/Support/ParseDateString.php @@ -29,6 +29,7 @@ use Carbon\CarbonInterface; use Carbon\Exceptions\InvalidFormatException; use FireflyIII\Exceptions\FireflyException; use Illuminate\Support\Facades\Log; + use function Safe\preg_match; /** @@ -78,15 +79,15 @@ class ParseDateString public function parseDate(string $date): Carbon { Log::debug(sprintf('parseDate("%s")', $date)); - $date = strtolower($date); + $date = strtolower($date); // parse keywords: if (in_array($date, $this->keywords, true)) { return $this->parseKeyword($date); } // if regex for YYYY-MM-DD: - $pattern = '/^(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01])$/'; - $result = preg_match($pattern, $date); + $pattern = '/^(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01])$/'; + $result = preg_match($pattern, $date); if (0 !== $result) { return $this->parseDefaultDate($date); } @@ -355,11 +356,11 @@ class ParseDateString foreach ($parts as $part) { Log::debug(sprintf('Now parsing part "%s"', $part)); - $part = trim($part); + $part = trim($part); // verify if correct - $pattern = '/[+-]\d+[wqmdy]/'; - $result = preg_match($pattern, $part); + $pattern = '/[+-]\d+[wqmdy]/'; + $result = preg_match($pattern, $part); if (0 === $result) { Log::error(sprintf('Part "%s" does not match regular expression. Will be skipped.', $part)); @@ -373,7 +374,7 @@ class ParseDateString continue; } - $func = $functions[$direction][$period]; + $func = $functions[$direction][$period]; Log::debug(sprintf('Will now do %s(%d) on %s', $func, $number, $today->format('Y-m-d'))); $today->{$func}($number); // @phpstan-ignore-line Log::debug(sprintf('Resulting date is %s', $today->format('Y-m-d'))); diff --git a/app/Support/Preferences.php b/app/Support/Preferences.php index f8fc423cc7..d622f1f66f 100644 --- a/app/Support/Preferences.php +++ b/app/Support/Preferences.php @@ -48,12 +48,13 @@ class Preferences } return Preference::where('user_id', $user->id) - ->where('name', '!=', 'currencyPreference') - ->where(function (Builder $q) use ($user): void { - $q->whereNull('user_group_id'); - $q->orWhere('user_group_id', $user->user_group_id); - }) - ->get(); + ->where('name', '!=', 'currencyPreference') + ->where(function (Builder $q) use ($user): void { + $q->whereNull('user_group_id'); + $q->orWhere('user_group_id', $user->user_group_id); + }) + ->get() + ; } public function beginsWith(User $user, string $search): Collection @@ -89,7 +90,7 @@ class Preferences Cache::put($key, '', 5); } - public function get(string $name, array | bool | int | string | null $default = null): ?Preference + public function get(string $name, array|bool|int|string|null $default = null): ?Preference { /** @var null|User $user */ $user = auth()->user(); @@ -107,12 +108,13 @@ class Preferences { $result = []; $preferences = Preference::where('user_id', $user->id) - ->where(function (Builder $q) use ($user): void { - $q->whereNull('user_group_id'); - $q->orWhere('user_group_id', $user->user_group_id); - }) - ->whereIn('name', $list) - ->get(['id', 'name', 'data']); + ->where(function (Builder $q) use ($user): void { + $q->whereNull('user_group_id'); + $q->orWhere('user_group_id', $user->user_group_id); + }) + ->whereIn('name', $list) + ->get(['id', 'name', 'data']) + ; /** @var Preference $preference */ foreach ($preferences as $preference) { @@ -154,7 +156,7 @@ class Preferences return $result; } - public function getEncryptedForUser(User $user, string $name, array | bool | int | string | null $default = null): ?Preference + public function getEncryptedForUser(User $user, string $name, array|bool|int|string|null $default = null): ?Preference { $result = $this->getForUser($user, $name, $default); if ('' === $result->data) { @@ -179,7 +181,7 @@ class Preferences return $result; } - public function getForUser(User $user, string $name, array | bool | int | string | null $default = null): ?Preference + public function getForUser(User $user, string $name, array|bool|int|string|null $default = null): ?Preference { // Log::debug(sprintf('getForUser(#%d, "%s")', $user->id, $name)); // don't care about user group ID, except for some specific preferences. @@ -190,7 +192,7 @@ class Preferences $query->where('user_group_id', $userGroupId); } - $preference = $query->first(['id', 'user_id', 'user_group_id', 'name', 'data', 'updated_at', 'created_at']); + $preference = $query->first(['id', 'user_id', 'user_group_id', 'name', 'data', 'updated_at', 'created_at']); if (null !== $preference && null === $preference->data) { $preference->delete(); @@ -214,7 +216,7 @@ class Preferences return $this->setForUser($user, $name, $default); } - public function getFresh(string $name, array | bool | int | string | null $default = null): ?Preference + public function getFresh(string $name, array|bool|int|string|null $default = null): ?Preference { /** @var null|User $user */ $user = auth()->user(); @@ -233,8 +235,8 @@ class Preferences */ public function lastActivity(): string { - $instance = PreferencesSingleton::getInstance(); - $pref = $instance->getPreference('last_activity'); + $instance = PreferencesSingleton::getInstance(); + $pref = $instance->getPreference('last_activity'); if (null !== $pref) { // Log::debug(sprintf('Found last activity in singleton: %s', $pref)); return $pref; @@ -248,7 +250,7 @@ class Preferences if (is_array($lastActivity)) { $lastActivity = implode(',', $lastActivity); } - $setting = hash('sha256', (string)$lastActivity); + $setting = hash('sha256', (string)$lastActivity); $instance->setPreference('last_activity', $setting); return $setting; @@ -262,7 +264,7 @@ class Preferences Session::forget('first'); } - public function set(string $name, array | bool | int | string | null $value): Preference + public function set(string $name, array|bool|int|string|null $value): Preference { /** @var null|User $user */ $user = auth()->user(); @@ -291,21 +293,21 @@ class Preferences return $this->set($name, $encrypted); } - public function setForUser(User $user, string $name, array | bool | int | string | null $value): Preference + public function setForUser(User $user, string $name, array|bool|int|string|null $value): Preference { - $fullName = sprintf('preference%s%s', $user->id, $name); - $userGroupId = $this->getUserGroupId($user, $name); - $userGroupId = 0 === (int)$userGroupId ? null : (int)$userGroupId; + $fullName = sprintf('preference%s%s', $user->id, $name); + $userGroupId = $this->getUserGroupId($user, $name); + $userGroupId = 0 === (int)$userGroupId ? null : (int)$userGroupId; Cache::forget($fullName); - $query = Preference::where('user_id', $user->id)->where('name', $name); + $query = Preference::where('user_id', $user->id)->where('name', $name); if (null !== $userGroupId) { Log::debug('Include user group ID in query'); $query->where('user_group_id', $userGroupId); } - $preference = $query->first(['id', 'user_id', 'user_group_id', 'name', 'data', 'updated_at', 'created_at']); + $preference = $query->first(['id', 'user_id', 'user_group_id', 'name', 'data', 'updated_at', 'created_at']); if (null !== $preference && null === $value) { $preference->delete(); diff --git a/app/Support/Report/Budget/BudgetReportGenerator.php b/app/Support/Report/Budget/BudgetReportGenerator.php index b859847748..2fd81217bb 100644 --- a/app/Support/Report/Budget/BudgetReportGenerator.php +++ b/app/Support/Report/Budget/BudgetReportGenerator.php @@ -76,7 +76,7 @@ class BudgetReportGenerator /** @var Account $account */ foreach ($this->accounts as $account) { - $accountId = $account->id; + $accountId = $account->id; $this->report[$accountId] ??= [ 'name' => $account->name, 'id' => $account->id, @@ -170,16 +170,16 @@ class BudgetReportGenerator 'budget_limits' => [], ]; - $noBudget = $this->nbRepository->sumExpenses($this->start, $this->end, $this->accounts); + $noBudget = $this->nbRepository->sumExpenses($this->start, $this->end, $this->accounts); foreach ($noBudget as $noBudgetEntry) { // currency information: - $nbCurrencyId = (int)($noBudgetEntry['currency_id'] ?? $this->currency->id); - $nbCurrencyCode = $noBudgetEntry['currency_code'] ?? $this->currency->code; - $nbCurrencyName = $noBudgetEntry['currency_name'] ?? $this->currency->name; - $nbCurrencySymbol = $noBudgetEntry['currency_symbol'] ?? $this->currency->symbol; - $nbCurrencyDp = $noBudgetEntry['currency_decimal_places'] ?? $this->currency->decimal_places; + $nbCurrencyId = (int)($noBudgetEntry['currency_id'] ?? $this->currency->id); + $nbCurrencyCode = $noBudgetEntry['currency_code'] ?? $this->currency->code; + $nbCurrencyName = $noBudgetEntry['currency_name'] ?? $this->currency->name; + $nbCurrencySymbol = $noBudgetEntry['currency_symbol'] ?? $this->currency->symbol; + $nbCurrencyDp = $noBudgetEntry['currency_decimal_places'] ?? $this->currency->decimal_places; - $this->report['budgets'][0]['budget_limits'][] = [ + $this->report['budgets'][0]['budget_limits'][] = [ 'budget_limit_id' => null, 'start_date' => $this->start, 'end_date' => $this->end, @@ -195,7 +195,7 @@ class BudgetReportGenerator 'currency_symbol' => $nbCurrencySymbol, 'currency_decimal_places' => $nbCurrencyDp, ]; - $this->report['sums'][$nbCurrencyId]['spent'] = bcadd($this->report['sums'][$nbCurrencyId]['spent'] ?? '0', (string)$noBudgetEntry['sum']); + $this->report['sums'][$nbCurrencyId]['spent'] = bcadd($this->report['sums'][$nbCurrencyId]['spent'] ?? '0', (string)$noBudgetEntry['sum']); // append currency info because it may be missing: $this->report['sums'][$nbCurrencyId]['currency_id'] = $nbCurrencyId; $this->report['sums'][$nbCurrencyId]['currency_code'] = $nbCurrencyCode; @@ -218,15 +218,15 @@ class BudgetReportGenerator // make percentages based on total amount. foreach ($this->report['budgets'] as $budgetId => $data) { foreach ($data['budget_limits'] as $limitId => $entry) { - $budgetId = (int)$budgetId; - $limitId = (int)$limitId; - $currencyId = (int)$entry['currency_id']; - $spent = $entry['spent']; - $totalSpent = $this->report['sums'][$currencyId]['spent'] ?? '0'; - $spentPct = '0'; - $budgeted = $entry['budgeted']; - $totalBudgeted = $this->report['sums'][$currencyId]['budgeted'] ?? '0'; - $budgetedPct = '0'; + $budgetId = (int)$budgetId; + $limitId = (int)$limitId; + $currencyId = (int)$entry['currency_id']; + $spent = $entry['spent']; + $totalSpent = $this->report['sums'][$currencyId]['spent'] ?? '0'; + $spentPct = '0'; + $budgeted = $entry['budgeted']; + $totalBudgeted = $this->report['sums'][$currencyId]['budgeted'] ?? '0'; + $budgetedPct = '0'; if (0 !== bccomp((string)$spent, '0') && 0 !== bccomp($totalSpent, '0')) { $spentPct = round((float)bcmul(bcdiv((string)$spent, $totalSpent), '100')); @@ -234,7 +234,7 @@ class BudgetReportGenerator if (0 !== bccomp((string)$budgeted, '0') && 0 !== bccomp($totalBudgeted, '0')) { $budgetedPct = round((float)bcmul(bcdiv((string)$budgeted, $totalBudgeted), '100')); } - $this->report['sums'][$currencyId]['budgeted'] ??= '0'; + $this->report['sums'][$currencyId]['budgeted'] ??= '0'; $this->report['budgets'][$budgetId]['budget_limits'][$limitId]['spent_pct'] = $spentPct; $this->report['budgets'][$budgetId]['budget_limits'][$limitId]['budgeted_pct'] = $budgetedPct; } @@ -246,7 +246,7 @@ class BudgetReportGenerator */ private function processBudget(Budget $budget): void { - $budgetId = $budget->id; + $budgetId = $budget->id; $this->report['budgets'][$budgetId] ??= [ 'budget_id' => $budgetId, 'budget_name' => $budget->name, @@ -255,7 +255,7 @@ class BudgetReportGenerator ]; // get all budget limits for budget in period: - $limits = $this->blRepository->getBudgetLimits($budget, $this->start, $this->end); + $limits = $this->blRepository->getBudgetLimits($budget, $this->start, $this->end); /** @var BudgetLimit $limit */ foreach ($limits as $limit) { @@ -275,18 +275,18 @@ class BudgetReportGenerator $this->report[$sourceAccountId]['currencies'][$currencyId] ??= [ - 'currency_id' => $expenses['currency_id'], - 'currency_symbol' => $expenses['currency_symbol'], - 'currency_name' => $expenses['currency_name'], - 'currency_decimal_places' => $expenses['currency_decimal_places'], - 'budgets' => [], - ]; + 'currency_id' => $expenses['currency_id'], + 'currency_symbol' => $expenses['currency_symbol'], + 'currency_name' => $expenses['currency_name'], + 'currency_decimal_places' => $expenses['currency_decimal_places'], + 'budgets' => [], + ]; $this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId] ??= '0'; $this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId] - = bcadd($this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId], (string)$journal['amount']); + = bcadd($this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId], (string)$journal['amount']); } } @@ -305,14 +305,14 @@ class BudgetReportGenerator */ private function processLimit(Budget $budget, BudgetLimit $limit): void { - $budgetId = $budget->id; - $limitId = $limit->id; - $limitCurrency = $limit->transactionCurrency ?? $this->currency; - $currencyId = $limitCurrency->id; - $expenses = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, $this->accounts, new Collection()->push($budget)); - $spent = $expenses[$currencyId]['sum'] ?? '0'; - $left = -1 === bccomp(bcadd($limit->amount, $spent), '0') ? '0' : bcadd($limit->amount, $spent); - $overspent = 1 === bccomp(bcmul($spent, '-1'), $limit->amount) ? bcadd($spent, $limit->amount) : '0'; + $budgetId = $budget->id; + $limitId = $limit->id; + $limitCurrency = $limit->transactionCurrency ?? $this->currency; + $currencyId = $limitCurrency->id; + $expenses = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, $this->accounts, new Collection()->push($budget)); + $spent = $expenses[$currencyId]['sum'] ?? '0'; + $left = -1 === bccomp(bcadd($limit->amount, $spent), '0') ? '0' : bcadd($limit->amount, $spent); + $overspent = 1 === bccomp(bcmul($spent, '-1'), $limit->amount) ? bcadd($spent, $limit->amount) : '0'; $this->report['budgets'][$budgetId]['budget_limits'][$limitId] ??= [ 'budget_limit_id' => $limitId, @@ -333,17 +333,17 @@ class BudgetReportGenerator // make sum information: $this->report['sums'][$currencyId] - ??= [ - 'budgeted' => '0', - 'spent' => '0', - 'left' => '0', - 'overspent' => '0', - 'currency_id' => $currencyId, - 'currency_code' => $limitCurrency->code, - 'currency_name' => $limitCurrency->name, - 'currency_symbol' => $limitCurrency->symbol, - 'currency_decimal_places' => $limitCurrency->decimal_places, - ]; + ??= [ + 'budgeted' => '0', + 'spent' => '0', + 'left' => '0', + 'overspent' => '0', + 'currency_id' => $currencyId, + 'currency_code' => $limitCurrency->code, + 'currency_name' => $limitCurrency->name, + 'currency_symbol' => $limitCurrency->symbol, + 'currency_decimal_places' => $limitCurrency->decimal_places, + ]; $this->report['sums'][$currencyId]['budgeted'] = bcadd((string)$this->report['sums'][$currencyId]['budgeted'], $limit->amount); $this->report['sums'][$currencyId]['spent'] = bcadd((string)$this->report['sums'][$currencyId]['spent'], $spent); $this->report['sums'][$currencyId]['left'] = bcadd((string)$this->report['sums'][$currencyId]['left'], bcadd($limit->amount, $spent)); diff --git a/app/Support/Report/Category/CategoryReportGenerator.php b/app/Support/Report/Category/CategoryReportGenerator.php index 8f800d411d..51570e7696 100644 --- a/app/Support/Report/Category/CategoryReportGenerator.php +++ b/app/Support/Report/Category/CategoryReportGenerator.php @@ -62,17 +62,17 @@ class CategoryReportGenerator */ public function operations(): void { - $earnedWith = $this->opsRepository->listIncome($this->start, $this->end, $this->accounts); - $spentWith = $this->opsRepository->listExpenses($this->start, $this->end, $this->accounts); + $earnedWith = $this->opsRepository->listIncome($this->start, $this->end, $this->accounts); + $spentWith = $this->opsRepository->listExpenses($this->start, $this->end, $this->accounts); // also transferred out and transferred into these accounts in this category: $transferredIn = $this->opsRepository->listTransferredIn($this->start, $this->end, $this->accounts); $transferredOut = $this->opsRepository->listTransferredOut($this->start, $this->end, $this->accounts); - $earnedWithout = $this->noCatRepository->listIncome($this->start, $this->end, $this->accounts); - $spentWithout = $this->noCatRepository->listExpenses($this->start, $this->end, $this->accounts); + $earnedWithout = $this->noCatRepository->listIncome($this->start, $this->end, $this->accounts); + $spentWithout = $this->noCatRepository->listExpenses($this->start, $this->end, $this->accounts); - $this->report = [ + $this->report = [ 'categories' => [], 'sums' => [], ]; @@ -106,7 +106,7 @@ class CategoryReportGenerator private function processCategoryRow(int $currencyId, array $currencyRow, int $categoryId, array $categoryRow): void { - $key = sprintf('%s-%s', $currencyId, $categoryId); + $key = sprintf('%s-%s', $currencyId, $categoryId); $this->report['categories'][$key] ??= [ 'id' => $categoryId, 'title' => $categoryRow['name'], @@ -122,9 +122,9 @@ class CategoryReportGenerator // loop journals: foreach ($categoryRow['transaction_journals'] as $journal) { // sum of sums - $this->report['sums'][$currencyId]['sum'] = bcadd((string)$this->report['sums'][$currencyId]['sum'], (string)$journal['amount']); + $this->report['sums'][$currencyId]['sum'] = bcadd((string)$this->report['sums'][$currencyId]['sum'], (string)$journal['amount']); // sum of spent: - $this->report['sums'][$currencyId]['spent'] = -1 === bccomp((string)$journal['amount'], '0') ? bcadd( + $this->report['sums'][$currencyId]['spent'] = -1 === bccomp((string)$journal['amount'], '0') ? bcadd( (string)$this->report['sums'][$currencyId]['spent'], (string)$journal['amount'] ) : $this->report['sums'][$currencyId]['spent']; @@ -135,14 +135,14 @@ class CategoryReportGenerator ) : $this->report['sums'][$currencyId]['earned']; // sum of category - $this->report['categories'][$key]['sum'] = bcadd((string)$this->report['categories'][$key]['sum'], (string)$journal['amount']); + $this->report['categories'][$key]['sum'] = bcadd((string)$this->report['categories'][$key]['sum'], (string)$journal['amount']); // total spent in category - $this->report['categories'][$key]['spent'] = -1 === bccomp((string)$journal['amount'], '0') ? bcadd( + $this->report['categories'][$key]['spent'] = -1 === bccomp((string)$journal['amount'], '0') ? bcadd( (string)$this->report['categories'][$key]['spent'], (string)$journal['amount'] ) : $this->report['categories'][$key]['spent']; // total earned in category - $this->report['categories'][$key]['earned'] = 1 === bccomp((string)$journal['amount'], '0') ? bcadd( + $this->report['categories'][$key]['earned'] = 1 === bccomp((string)$journal['amount'], '0') ? bcadd( (string)$this->report['categories'][$key]['earned'], (string)$journal['amount'] ) : $this->report['categories'][$key]['earned']; diff --git a/app/Support/Report/Summarizer/TransactionSummarizer.php b/app/Support/Report/Summarizer/TransactionSummarizer.php index 84e0ec3231..5660a239f0 100644 --- a/app/Support/Report/Summarizer/TransactionSummarizer.php +++ b/app/Support/Report/Summarizer/TransactionSummarizer.php @@ -48,14 +48,14 @@ class TransactionSummarizer Log::debug(sprintf('Now in groupByCurrencyId([%d journals], "%s", %s)', count($journals), $method, var_export($includeForeign, true))); $array = []; foreach ($journals as $journal) { - $field = 'amount'; + $field = 'amount'; // grab default currency information. - $currencyId = (int)$journal['currency_id']; - $currencyName = $journal['currency_name']; - $currencySymbol = $journal['currency_symbol']; - $currencyCode = $journal['currency_code']; - $currencyDecimalPlaces = $journal['currency_decimal_places']; + $currencyId = (int)$journal['currency_id']; + $currencyName = $journal['currency_name']; + $currencySymbol = $journal['currency_symbol']; + $currencyCode = $journal['currency_code']; + $currencyDecimalPlaces = $journal['currency_decimal_places']; // prepare foreign currency info: $foreignCurrencyId = 0; @@ -102,7 +102,7 @@ class TransactionSummarizer } // first process normal amount - $amount = (string)($journal[$field] ?? '0'); + $amount = (string)($journal[$field] ?? '0'); $array[$currencyId] ??= [ 'sum' => '0', 'currency_id' => $currencyId, @@ -121,7 +121,7 @@ class TransactionSummarizer // then process foreign amount, if it exists. if (0 !== $foreignCurrencyId && true === $includeForeign) { - $amount = (string)($journal['foreign_amount'] ?? '0'); + $amount = (string)($journal['foreign_amount'] ?? '0'); $array[$foreignCurrencyId] ??= [ 'sum' => '0', 'currency_id' => $foreignCurrencyId, @@ -179,7 +179,7 @@ class TransactionSummarizer if ($convertToPrimary && $journal['currency_id'] !== $primary->id && $primary->id === $journal['foreign_currency_id']) { $field = 'foreign_amount'; } - $key = sprintf('%s-%s', $journal[$idKey], $currencyId); + $key = sprintf('%s-%s', $journal[$idKey], $currencyId); // sum it all up or create a new array. $array[$key] ??= [ 'id' => $journal[$idKey], @@ -193,7 +193,7 @@ class TransactionSummarizer ]; // add the data from the $field to the array. - $array[$key]['sum'] = bcadd($array[$key]['sum'], Steam::{$method}((string)($journal[$field] ?? '0'))); // @phpstan-ignore-line + $array[$key]['sum'] = bcadd($array[$key]['sum'], Steam::{$method}((string)($journal[$field] ?? '0'))); // @phpstan-ignore-line Log::debug(sprintf('Field for transaction #%d is "%s" (%s). Sum: %s', $journal['transaction_group_id'], $currencyCode, $field, $array[$key]['sum'])); // also do foreign amount, but only when convertToPrimary is false (otherwise we have it already) @@ -201,7 +201,7 @@ class TransactionSummarizer if ((!$convertToPrimary || $journal['foreign_currency_id'] !== $primary->id) && 0 !== (int)$journal['foreign_currency_id']) { Log::debug(sprintf('Use foreign amount from transaction #%d: %s %s. Sum: %s', $journal['transaction_group_id'], $currencyCode, $journal['foreign_amount'], $array[$key]['sum'])); $key = sprintf('%s-%s', $journal[$idKey], $journal['foreign_currency_id']); - $array[$key] ??= [ + $array[$key] ??= [ 'id' => $journal[$idKey], 'name' => $journal[$nameKey], 'sum' => '0', diff --git a/app/Support/Repositories/Recurring/CalculateRangeOccurrences.php b/app/Support/Repositories/Recurring/CalculateRangeOccurrences.php index 4ca2c8c29e..df73a72f60 100644 --- a/app/Support/Repositories/Recurring/CalculateRangeOccurrences.php +++ b/app/Support/Repositories/Recurring/CalculateRangeOccurrences.php @@ -82,8 +82,8 @@ trait CalculateRangeOccurrences */ protected function getNdomInRange(Carbon $start, Carbon $end, int $skipMod, string $moment): array { - $return = []; - $attempts = 0; + $return = []; + $attempts = 0; $start->startOfMonth(); // this feels a bit like a cop out but why reinvent the wheel? $counters = [1 => 'first', 2 => 'second', 3 => 'third', 4 => 'fourth', 5 => 'fifth']; @@ -108,12 +108,12 @@ trait CalculateRangeOccurrences */ protected function getWeeklyInRange(Carbon $start, Carbon $end, int $skipMod, string $moment): array { - $return = []; - $attempts = 0; + $return = []; + $attempts = 0; app('log')->debug('Rep is weekly.'); // monday = 1 // sunday = 7 - $dayOfWeek = (int)$moment; + $dayOfWeek = (int)$moment; app('log')->debug(sprintf('DoW in repetition is %d, in mutator is %d', $dayOfWeek, $start->dayOfWeekIso)); if ($start->dayOfWeekIso > $dayOfWeek) { // day has already passed this week, add one week: @@ -154,8 +154,8 @@ trait CalculateRangeOccurrences } // is $date between $start and $end? - $obj = clone $date; - $count = 0; + $obj = clone $date; + $count = 0; while ($obj <= $end && $obj >= $start && $count < 10) { if (0 === $attempts % $skipMod) { $return[] = clone $obj; diff --git a/app/Support/Repositories/Recurring/CalculateXOccurrences.php b/app/Support/Repositories/Recurring/CalculateXOccurrences.php index 602cb03d02..f31171810f 100644 --- a/app/Support/Repositories/Recurring/CalculateXOccurrences.php +++ b/app/Support/Repositories/Recurring/CalculateXOccurrences.php @@ -89,10 +89,10 @@ trait CalculateXOccurrences */ protected function getXNDomOccurrences(Carbon $date, int $count, int $skipMod, string $moment): array { - $return = []; - $total = 0; - $attempts = 0; - $mutator = clone $date; + $return = []; + $total = 0; + $attempts = 0; + $mutator = clone $date; $mutator->addDay(); // always assume today has passed. $mutator->startOfMonth(); // this feels a bit like a cop out but why reinvent the wheel? @@ -120,14 +120,14 @@ trait CalculateXOccurrences */ protected function getXWeeklyOccurrences(Carbon $date, int $count, int $skipMod, string $moment): array { - $return = []; - $total = 0; - $attempts = 0; - $mutator = clone $date; + $return = []; + $total = 0; + $attempts = 0; + $mutator = clone $date; // monday = 1 // sunday = 7 $mutator->addDay(); // always assume today has passed. - $dayOfWeek = (int)$moment; + $dayOfWeek = (int)$moment; if ($mutator->dayOfWeekIso > $dayOfWeek) { // day has already passed this week, add one week: $mutator->addWeek(); @@ -164,7 +164,7 @@ trait CalculateXOccurrences if ($mutator > $date) { $date->addYear(); } - $obj = clone $date; + $obj = clone $date; while ($total < $count) { if (0 === $attempts % $skipMod) { $return[] = clone $obj; diff --git a/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php b/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php index bd4b44fd7d..2fc216493d 100644 --- a/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php +++ b/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php @@ -87,7 +87,7 @@ trait CalculateXOccurrencesSince ++$total; } ++$attempts; - $mutator = $mutator->endOfMonth()->addDay(); + $mutator = $mutator->endOfMonth()->addDay(); } Log::debug('Collected enough occurrences.'); @@ -103,10 +103,10 @@ trait CalculateXOccurrencesSince protected function getXNDomOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array { Log::debug(sprintf('Now in %s', __METHOD__)); - $return = []; - $total = 0; - $attempts = 0; - $mutator = clone $date; + $return = []; + $total = 0; + $attempts = 0; + $mutator = clone $date; $mutator->addDay(); // always assume today has passed. $mutator->startOfMonth(); // this feels a bit like a cop out but why reinvent the wheel? @@ -137,15 +137,15 @@ trait CalculateXOccurrencesSince protected function getXWeeklyOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array { Log::debug(sprintf('Now in %s', __METHOD__)); - $return = []; - $total = 0; - $attempts = 0; - $mutator = clone $date; + $return = []; + $total = 0; + $attempts = 0; + $mutator = clone $date; // monday = 1 // sunday = 7 // Removed assumption today has passed, see issue https://github.com/firefly-iii/firefly-iii/issues/4798 // $mutator->addDay(); // always assume today has passed. - $dayOfWeek = (int)$moment; + $dayOfWeek = (int)$moment; if ($mutator->dayOfWeekIso > $dayOfWeek) { // day has already passed this week, add one week: $mutator->addWeek(); @@ -189,7 +189,7 @@ trait CalculateXOccurrencesSince $date->addYear(); Log::debug(sprintf('Date is now %s', $date->format('Y-m-d'))); } - $obj = clone $date; + $obj = clone $date; while ($total < $count) { Log::debug(sprintf('total (%d) < count (%d) so go.', $total, $count)); Log::debug(sprintf('attempts (%d) %% skipmod (%d) === %d', $attempts, $skipMod, $attempts % $skipMod)); diff --git a/app/Support/Repositories/Recurring/FiltersWeekends.php b/app/Support/Repositories/Recurring/FiltersWeekends.php index 886679ced1..508e13f638 100644 --- a/app/Support/Repositories/Recurring/FiltersWeekends.php +++ b/app/Support/Repositories/Recurring/FiltersWeekends.php @@ -46,7 +46,7 @@ trait FiltersWeekends return $dates; } - $return = []; + $return = []; /** @var Carbon $date */ foreach ($dates as $date) { @@ -60,7 +60,7 @@ trait FiltersWeekends // is weekend and must set back to Friday? if (RecurrenceRepetitionWeekend::WEEKEND_TO_FRIDAY->value === $repetition->weekend) { - $clone = clone $date; + $clone = clone $date; $clone->addDays(5 - $date->dayOfWeekIso); Log::debug( sprintf('Date is %s, and this is in the weekend, so corrected to %s (Friday).', $date->format('D d M Y'), $clone->format('D d M Y')) @@ -72,7 +72,7 @@ trait FiltersWeekends // postpone to Monday? if (RecurrenceRepetitionWeekend::WEEKEND_TO_MONDAY->value === $repetition->weekend) { - $clone = clone $date; + $clone = clone $date; $clone->addDays(8 - $date->dayOfWeekIso); Log::debug( sprintf('Date is %s, and this is in the weekend, so corrected to %s (Monday).', $date->format('D d M Y'), $clone->format('D d M Y')) diff --git a/app/Support/Repositories/UserGroup/UserGroupInterface.php b/app/Support/Repositories/UserGroup/UserGroupInterface.php index 67e7fe3ea3..d7a737b919 100644 --- a/app/Support/Repositories/UserGroup/UserGroupInterface.php +++ b/app/Support/Repositories/UserGroup/UserGroupInterface.php @@ -37,7 +37,7 @@ interface UserGroupInterface public function getUserGroup(): ?UserGroup; - public function setUser(Authenticatable | User | null $user): void; + public function setUser(Authenticatable|User|null $user): void; public function setUserGroup(UserGroup $userGroup): void; diff --git a/app/Support/Repositories/UserGroup/UserGroupTrait.php b/app/Support/Repositories/UserGroup/UserGroupTrait.php index b6a1c94f5a..98781e5596 100644 --- a/app/Support/Repositories/UserGroup/UserGroupTrait.php +++ b/app/Support/Repositories/UserGroup/UserGroupTrait.php @@ -61,10 +61,10 @@ trait UserGroupTrait /** * @throws FireflyException */ - public function setUser(Authenticatable | User | null $user): void + public function setUser(Authenticatable|User|null $user): void { if ($user instanceof User) { - $this->user = $user; + $this->user = $user; if (null === $user->userGroup) { throw new FireflyException(sprintf('User #%d ("%s") has no user group.', $user->id, $user->email)); } @@ -99,14 +99,15 @@ trait UserGroupTrait public function setUserGroupById(int $userGroupId): void { $memberships = GroupMembership::where('user_id', $this->user->id) - ->where('user_group_id', $userGroupId) - ->count(); + ->where('user_group_id', $userGroupId) + ->count() + ; if (0 === $memberships) { throw new FireflyException(sprintf('User #%d has no access to administration #%d', $this->user->id, $userGroupId)); } /** @var null|UserGroup $userGroup */ - $userGroup = UserGroup::find($userGroupId); + $userGroup = UserGroup::find($userGroupId); if (null === $userGroup) { throw new FireflyException(sprintf('Cannot find administration for user #%d', $this->user->id)); } diff --git a/app/Support/Request/AppendsLocationData.php b/app/Support/Request/AppendsLocationData.php index 01a6de40d3..b77cd9ee41 100644 --- a/app/Support/Request/AppendsLocationData.php +++ b/app/Support/Request/AppendsLocationData.php @@ -96,12 +96,12 @@ trait AppendsLocationData $data['latitude'] = null; $data['zoom_level'] = null; - $longitudeKey = $this->getLocationKey($prefix, 'longitude'); - $latitudeKey = $this->getLocationKey($prefix, 'latitude'); - $zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level'); - $isValidPOST = $this->isValidPost($prefix); - $isValidPUT = $this->isValidPUT($prefix); - $isValidEmptyPUT = $this->isValidEmptyPUT($prefix); + $longitudeKey = $this->getLocationKey($prefix, 'longitude'); + $latitudeKey = $this->getLocationKey($prefix, 'latitude'); + $zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level'); + $isValidPOST = $this->isValidPost($prefix); + $isValidPUT = $this->isValidPUT($prefix); + $isValidEmptyPUT = $this->isValidEmptyPUT($prefix); // for a POST (store), all fields must be present and not NULL. if ($isValidPOST) { @@ -153,9 +153,9 @@ trait AppendsLocationData $zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level'); return ( - null === $this->get($longitudeKey) - && null === $this->get($latitudeKey) - && null === $this->get($zoomLevelKey)) + null === $this->get($longitudeKey) + && null === $this->get($latitudeKey) + && null === $this->get($zoomLevelKey)) && ( 'PUT' === $this->method() || ('POST' === $this->method() && $this->routeIs('*.update')) diff --git a/app/Support/Request/ChecksLogin.php b/app/Support/Request/ChecksLogin.php index 8576b17473..0f9753ee62 100644 --- a/app/Support/Request/ChecksLogin.php +++ b/app/Support/Request/ChecksLogin.php @@ -40,7 +40,7 @@ trait ChecksLogin { app('log')->debug(sprintf('Now in %s', __METHOD__)); // Only allow logged-in users - $check = auth()->check(); + $check = auth()->check(); if (!$check) { return false; } @@ -79,7 +79,7 @@ trait ChecksLogin public function getUserGroup(): ?UserGroup { /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); app('log')->debug('Now in getUserGroup()'); /** @var null|UserGroup $userGroup */ @@ -91,7 +91,7 @@ trait ChecksLogin app('log')->debug(sprintf('Request class has no user_group_id parameter, grab default from user (group #%d).', $user->user_group_id)); $userGroupId = (int)$user->user_group_id; } - $userGroup = UserGroup::find($userGroupId); + $userGroup = UserGroup::find($userGroupId); if (null === $userGroup) { app('log')->error(sprintf('Request class has user_group_id (#%d), but group does not exist.', $userGroupId)); diff --git a/app/Support/Request/ConvertsDataTypes.php b/app/Support/Request/ConvertsDataTypes.php index aa3896eb72..5293a6f804 100644 --- a/app/Support/Request/ConvertsDataTypes.php +++ b/app/Support/Request/ConvertsDataTypes.php @@ -31,6 +31,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\Facades\Steam; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; + use function Safe\preg_replace; /** @@ -147,15 +148,15 @@ trait ConvertsDataTypes public function convertSortParameters(string $field, string $class): array { // assume this all works, because the validator would have caught any errors. - $parameter = (string)request()->query->get($field); + $parameter = (string)request()->query->get($field); if ('' === $parameter) { return []; } $parts = explode(',', $parameter); $sortParameters = []; foreach ($parts as $part) { - $part = trim($part); - $direction = 'asc'; + $part = trim($part); + $direction = 'asc'; if ('-' === $part[0]) { $part = substr($part, 1); $direction = 'desc'; @@ -459,7 +460,7 @@ trait ConvertsDataTypes if (!is_array($entry)) { continue; } - $amount = null; + $amount = null; if (array_key_exists('current_amount', $entry)) { $amount = $this->clearString((string)($entry['current_amount'] ?? '0')); if (null === $entry['current_amount']) { diff --git a/app/Support/Request/ValidatesWebhooks.php b/app/Support/Request/ValidatesWebhooks.php index dff1541fde..15d41f41ef 100644 --- a/app/Support/Request/ValidatesWebhooks.php +++ b/app/Support/Request/ValidatesWebhooks.php @@ -40,9 +40,9 @@ trait ValidatesWebhooks if (count($validator->failed()) > 0) { return; } - $data = $validator->getData(); - $triggers = $data['triggers'] ?? []; - $responses = $data['responses'] ?? []; + $data = $validator->getData(); + $triggers = $data['triggers'] ?? []; + $responses = $data['responses'] ?? []; if (0 === count($triggers) || 0 === count($responses)) { Log::debug('No trigger or response, return.'); diff --git a/app/Support/Search/AccountSearch.php b/app/Support/Search/AccountSearch.php index fe6e817c72..44bb34e893 100644 --- a/app/Support/Search/AccountSearch.php +++ b/app/Support/Search/AccountSearch.php @@ -28,6 +28,7 @@ use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Collection; + use function Safe\json_encode; /** @@ -36,16 +37,16 @@ use function Safe\json_encode; class AccountSearch implements GenericSearchInterface { /** @var string */ - public const string SEARCH_ALL = 'all'; + public const string SEARCH_ALL = 'all'; /** @var string */ - public const string SEARCH_IBAN = 'iban'; + public const string SEARCH_IBAN = 'iban'; /** @var string */ - public const string SEARCH_ID = 'id'; + public const string SEARCH_ID = 'id'; /** @var string */ - public const string SEARCH_NAME = 'name'; + public const string SEARCH_NAME = 'name'; /** @var string */ public const string SEARCH_NUMBER = 'number'; @@ -62,9 +63,10 @@ class AccountSearch implements GenericSearchInterface public function search(): Collection { $searchQuery = $this->user->accounts() - ->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id') - ->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id') - ->whereIn('account_types.type', $this->types); + ->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id') + ->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id') + ->whereIn('account_types.type', $this->types) + ; $like = sprintf('%%%s%%', $this->query); $originalQuery = $this->query; @@ -135,7 +137,7 @@ class AccountSearch implements GenericSearchInterface $this->types = $types; } - public function setUser(Authenticatable | User | null $user): void + public function setUser(Authenticatable|User|null $user): void { if ($user instanceof User) { $this->user = $user; diff --git a/app/Support/Search/OperatorQuerySearch.php b/app/Support/Search/OperatorQuerySearch.php index 2e813bd125..5b2cc0776c 100644 --- a/app/Support/Search/OperatorQuerySearch.php +++ b/app/Support/Search/OperatorQuerySearch.php @@ -117,7 +117,7 @@ class OperatorQuerySearch implements SearchInterface $operator = substr($operator, 1); } - $config = config(sprintf('search.operators.%s', $operator)); + $config = config(sprintf('search.operators.%s', $operator)); if (null === $config) { throw new FireflyException(sprintf('No configuration for search operator "%s"', $operator)); } @@ -186,7 +186,7 @@ class OperatorQuerySearch implements SearchInterface try { $parsedQuery = $parser->parse($query); - } catch (LogicException | TypeError $e) { + } catch (LogicException|TypeError $e) { Log::error($e->getMessage()); Log::error(sprintf('Could not parse search: "%s".', $query)); @@ -278,7 +278,7 @@ class OperatorQuerySearch implements SearchInterface $value = $node->getValue(); $prohibited = $node->isProhibited($flipProhibitedFlag); - $context = config(sprintf('search.operators.%s.needs_context', $operator)); + $context = config(sprintf('search.operators.%s.needs_context', $operator)); // is an operator that needs no context, and value is false, then prohibited = true. if ('false' === $value && in_array($operator, $this->validOperators, true) && false === $context && !$prohibited) { @@ -292,14 +292,14 @@ class OperatorQuerySearch implements SearchInterface } // must be valid operator: - $inArray = in_array($operator, $this->validOperators, true); + $inArray = in_array($operator, $this->validOperators, true); if ($inArray) { if ($this->updateCollector($operator, $value, $prohibited)) { $this->operators->push([ - 'type' => self::getRootOperator($operator), - 'value' => $value, - 'prohibited' => $prohibited, - ]); + 'type' => self::getRootOperator($operator), + 'value' => $value, + 'prohibited' => $prohibited, + ]); Log::debug(sprintf('Added operator type "%s"', $operator)); } } @@ -355,7 +355,7 @@ class OperatorQuerySearch implements SearchInterface private function handleStringNode(StringNode $node, bool $flipProhibitedFlag): void { - $string = $node->getValue(); + $string = $node->getValue(); $prohibited = $node->isProhibited($flipProhibitedFlag); @@ -477,7 +477,7 @@ class OperatorQuerySearch implements SearchInterface } } // string position (default): starts with: - $stringMethod = 'str_starts_with'; + $stringMethod = 'str_starts_with'; // string position: ends with: if (StringPosition::ENDS === $stringPosition) { @@ -491,7 +491,7 @@ class OperatorQuerySearch implements SearchInterface } // get accounts: - $accounts = $this->accountRepository->searchAccount($value, $searchTypes, 1337); + $accounts = $this->accountRepository->searchAccount($value, $searchTypes, 1337); if (0 === $accounts->count() && false === $prohibited) { Log::warning('Found zero accounts, search for non existing account, NO results will be returned.'); $this->collector->findNothing(); @@ -504,8 +504,8 @@ class OperatorQuerySearch implements SearchInterface return; } Log::debug(sprintf('Found %d accounts, will filter.', $accounts->count())); - $filtered = $accounts->filter( - static fn(Account $account) => $stringMethod(strtolower($account->name), strtolower($value)) + $filtered = $accounts->filter( + static fn (Account $account) => $stringMethod(strtolower($account->name), strtolower($value)) ); if (0 === $filtered->count()) { @@ -557,7 +557,7 @@ class OperatorQuerySearch implements SearchInterface } // string position (default): starts with: - $stringMethod = 'str_starts_with'; + $stringMethod = 'str_starts_with'; // string position: ends with: if (StringPosition::ENDS === $stringPosition) { @@ -571,7 +571,7 @@ class OperatorQuerySearch implements SearchInterface } // search for accounts: - $accounts = $this->accountRepository->searchAccountNr($value, $searchTypes, 1337); + $accounts = $this->accountRepository->searchAccountNr($value, $searchTypes, 1337); if (0 === $accounts->count()) { Log::debug('Found zero accounts, search for invalid account.'); Log::warning('Call to findNothing() from searchAccountNr().'); @@ -582,7 +582,7 @@ class OperatorQuerySearch implements SearchInterface // if found, do filter Log::debug(sprintf('Found %d accounts, will filter.', $accounts->count())); - $filtered = $accounts->filter( + $filtered = $accounts->filter( static function (Account $account) use ($value, $stringMethod) { // either IBAN or account number $ibanMatch = $stringMethod(strtolower((string)$account->iban), strtolower($value)); @@ -1250,15 +1250,15 @@ class OperatorQuerySearch implements SearchInterface throw new FireflyException(sprintf('Unsupported search operator: "%s"', $operator)); - // some search operators are ignored, basically: + // some search operators are ignored, basically: case 'user_action': Log::info(sprintf('Ignore search operator "%s"', $operator)); return false; - // - // all account related searches: - // + // + // all account related searches: + // case 'account_is': $this->searchAccount($value, SearchDirection::BOTH, StringPosition::IS); @@ -1420,7 +1420,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'source_account_id': - $account = $this->accountRepository->find((int)$value); + $account = $this->accountRepository->find((int)$value); if (null !== $account) { $this->collector->setSourceAccounts(new Collection()->push($account)); } @@ -1433,7 +1433,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-source_account_id': - $account = $this->accountRepository->find((int)$value); + $account = $this->accountRepository->find((int)$value); if (null !== $account) { $this->collector->excludeSourceAccounts(new Collection()->push($account)); } @@ -1446,25 +1446,25 @@ class OperatorQuerySearch implements SearchInterface break; case 'journal_id': - $parts = explode(',', $value); + $parts = explode(',', $value); $this->collector->setJournalIds($parts); break; case '-journal_id': - $parts = explode(',', $value); + $parts = explode(',', $value); $this->collector->excludeJournalIds($parts); break; case 'id': - $parts = explode(',', $value); + $parts = explode(',', $value); $this->collector->setIds($parts); break; case '-id': - $parts = explode(',', $value); + $parts = explode(',', $value); $this->collector->excludeIds($parts); break; @@ -1550,7 +1550,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'destination_account_id': - $account = $this->accountRepository->find((int)$value); + $account = $this->accountRepository->find((int)$value); if (null !== $account) { $this->collector->setDestinationAccounts(new Collection()->push($account)); } @@ -1562,7 +1562,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-destination_account_id': - $account = $this->accountRepository->find((int)$value); + $account = $this->accountRepository->find((int)$value); if (null !== $account) { $this->collector->excludeDestinationAccounts(new Collection()->push($account)); } @@ -1575,12 +1575,12 @@ class OperatorQuerySearch implements SearchInterface case 'account_id': Log::debug(sprintf('Now in "account_id" with value "%s"', $value)); - $parts = explode(',', $value); - $collection = new Collection(); + $parts = explode(',', $value); + $collection = new Collection(); foreach ($parts as $accountId) { $accountId = (int)$accountId; Log::debug(sprintf('Searching for account with ID #%d', $accountId)); - $account = $this->accountRepository->find($accountId); + $account = $this->accountRepository->find($accountId); if (null !== $account) { Log::debug(sprintf('Found account with ID #%d ("%s")', $accountId, $account->name)); $collection->push($account); @@ -1601,8 +1601,8 @@ class OperatorQuerySearch implements SearchInterface break; case '-account_id': - $parts = explode(',', $value); - $collection = new Collection(); + $parts = explode(',', $value); + $collection = new Collection(); foreach ($parts as $accountId) { $account = $this->accountRepository->find((int)$accountId); if (null !== $account) { @@ -1619,48 +1619,48 @@ class OperatorQuerySearch implements SearchInterface break; - // - // cash account - // + // + // cash account + // case 'source_is_cash': - $account = $this->getCashAccount(); + $account = $this->getCashAccount(); $this->collector->setSourceAccounts(new Collection()->push($account)); break; case '-source_is_cash': - $account = $this->getCashAccount(); + $account = $this->getCashAccount(); $this->collector->excludeSourceAccounts(new Collection()->push($account)); break; case 'destination_is_cash': - $account = $this->getCashAccount(); + $account = $this->getCashAccount(); $this->collector->setDestinationAccounts(new Collection()->push($account)); break; case '-destination_is_cash': - $account = $this->getCashAccount(); + $account = $this->getCashAccount(); $this->collector->excludeDestinationAccounts(new Collection()->push($account)); break; case 'account_is_cash': - $account = $this->getCashAccount(); + $account = $this->getCashAccount(); $this->collector->setAccounts(new Collection()->push($account)); break; case '-account_is_cash': - $account = $this->getCashAccount(); + $account = $this->getCashAccount(); $this->collector->excludeAccounts(new Collection()->push($account)); break; - // - // description - // + // + // description + // case 'description_starts': $this->collector->descriptionStarts([$value]); @@ -1682,7 +1682,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'description_contains': - $this->words[] = $value; + $this->words[] = $value; return false; @@ -1701,11 +1701,11 @@ class OperatorQuerySearch implements SearchInterface break; - // - // currency - // + // + // currency + // case 'currency_is': - $currency = $this->findCurrency($value); + $currency = $this->findCurrency($value); if ($currency instanceof TransactionCurrency) { $this->collector->setCurrency($currency); } @@ -1717,7 +1717,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-currency_is': - $currency = $this->findCurrency($value); + $currency = $this->findCurrency($value); if ($currency instanceof TransactionCurrency) { $this->collector->excludeCurrency($currency); } @@ -1729,7 +1729,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'foreign_currency_is': - $currency = $this->findCurrency($value); + $currency = $this->findCurrency($value); if ($currency instanceof TransactionCurrency) { $this->collector->setForeignCurrency($currency); } @@ -1741,7 +1741,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-foreign_currency_is': - $currency = $this->findCurrency($value); + $currency = $this->findCurrency($value); if ($currency instanceof TransactionCurrency) { $this->collector->excludeForeignCurrency($currency); } @@ -1752,9 +1752,9 @@ class OperatorQuerySearch implements SearchInterface break; - // - // attachments - // + // + // attachments + // case 'has_attachments': case '-has_no_attachments': Log::debug('Set collector to filter on attachments.'); @@ -1769,8 +1769,8 @@ class OperatorQuerySearch implements SearchInterface break; - // - // categories + // + // categories case '-has_any_category': case 'has_no_category': $this->collector->withoutCategory(); @@ -1784,7 +1784,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'category_is': - $category = $this->categoryRepository->findByName($value); + $category = $this->categoryRepository->findByName($value); if (null !== $category) { $this->collector->setCategory($category); @@ -1796,7 +1796,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-category_is': - $category = $this->categoryRepository->findByName($value); + $category = $this->categoryRepository->findByName($value); if (null !== $category) { $this->collector->excludeCategory($category); @@ -1806,7 +1806,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'category_ends': - $result = $this->categoryRepository->categoryEndsWith($value, 1337); + $result = $this->categoryRepository->categoryEndsWith($value, 1337); if ($result->count() > 0) { $this->collector->setCategories($result); } @@ -1818,7 +1818,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-category_ends': - $result = $this->categoryRepository->categoryEndsWith($value, 1337); + $result = $this->categoryRepository->categoryEndsWith($value, 1337); if ($result->count() > 0) { $this->collector->excludeCategories($result); } @@ -1830,7 +1830,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'category_starts': - $result = $this->categoryRepository->categoryStartsWith($value, 1337); + $result = $this->categoryRepository->categoryStartsWith($value, 1337); if ($result->count() > 0) { $this->collector->setCategories($result); } @@ -1842,7 +1842,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-category_starts': - $result = $this->categoryRepository->categoryStartsWith($value, 1337); + $result = $this->categoryRepository->categoryStartsWith($value, 1337); if ($result->count() > 0) { $this->collector->excludeCategories($result); } @@ -1854,7 +1854,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'category_contains': - $result = $this->categoryRepository->searchCategory($value, 1337); + $result = $this->categoryRepository->searchCategory($value, 1337); if ($result->count() > 0) { $this->collector->setCategories($result); } @@ -1866,7 +1866,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-category_contains': - $result = $this->categoryRepository->searchCategory($value, 1337); + $result = $this->categoryRepository->searchCategory($value, 1337); if ($result->count() > 0) { $this->collector->excludeCategories($result); } @@ -1877,9 +1877,9 @@ class OperatorQuerySearch implements SearchInterface break; - // - // budgets - // + // + // budgets + // case '-has_any_budget': case 'has_no_budget': $this->collector->withoutBudget(); @@ -1893,7 +1893,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'budget_contains': - $result = $this->budgetRepository->searchBudget($value, 1337); + $result = $this->budgetRepository->searchBudget($value, 1337); if ($result->count() > 0) { $this->collector->setBudgets($result); } @@ -1905,7 +1905,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-budget_contains': - $result = $this->budgetRepository->searchBudget($value, 1337); + $result = $this->budgetRepository->searchBudget($value, 1337); if ($result->count() > 0) { $this->collector->excludeBudgets($result); } @@ -1917,7 +1917,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'budget_is': - $budget = $this->budgetRepository->findByName($value); + $budget = $this->budgetRepository->findByName($value); if (null !== $budget) { $this->collector->setBudget($budget); @@ -1929,7 +1929,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-budget_is': - $budget = $this->budgetRepository->findByName($value); + $budget = $this->budgetRepository->findByName($value); if (null !== $budget) { $this->collector->excludeBudget($budget); @@ -1941,7 +1941,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'budget_ends': - $result = $this->budgetRepository->budgetEndsWith($value, 1337); + $result = $this->budgetRepository->budgetEndsWith($value, 1337); if ($result->count() > 0) { $this->collector->setBudgets($result); } @@ -1953,7 +1953,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-budget_ends': - $result = $this->budgetRepository->budgetEndsWith($value, 1337); + $result = $this->budgetRepository->budgetEndsWith($value, 1337); if ($result->count() > 0) { $this->collector->excludeBudgets($result); } @@ -1965,7 +1965,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'budget_starts': - $result = $this->budgetRepository->budgetStartsWith($value, 1337); + $result = $this->budgetRepository->budgetStartsWith($value, 1337); if ($result->count() > 0) { $this->collector->setBudgets($result); } @@ -1977,7 +1977,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-budget_starts': - $result = $this->budgetRepository->budgetStartsWith($value, 1337); + $result = $this->budgetRepository->budgetStartsWith($value, 1337); if ($result->count() > 0) { $this->collector->excludeBudgets($result); } @@ -1988,9 +1988,9 @@ class OperatorQuerySearch implements SearchInterface break; - // - // bill - // + // + // bill + // case '-has_any_bill': case 'has_no_bill': $this->collector->withoutBill(); @@ -2004,7 +2004,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'bill_contains': - $result = $this->billRepository->searchBill($value, 1337); + $result = $this->billRepository->searchBill($value, 1337); if ($result->count() > 0) { $this->collector->setBills($result); @@ -2016,7 +2016,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-bill_contains': - $result = $this->billRepository->searchBill($value, 1337); + $result = $this->billRepository->searchBill($value, 1337); if ($result->count() > 0) { $this->collector->excludeBills($result); @@ -2028,7 +2028,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'bill_is': - $bill = $this->billRepository->findByName($value); + $bill = $this->billRepository->findByName($value); if (null !== $bill) { $this->collector->setBill($bill); @@ -2040,7 +2040,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-bill_is': - $bill = $this->billRepository->findByName($value); + $bill = $this->billRepository->findByName($value); if (null !== $bill) { $this->collector->excludeBills(new Collection()->push($bill)); @@ -2052,7 +2052,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'bill_ends': - $result = $this->billRepository->billEndsWith($value, 1337); + $result = $this->billRepository->billEndsWith($value, 1337); if ($result->count() > 0) { $this->collector->setBills($result); } @@ -2064,7 +2064,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-bill_ends': - $result = $this->billRepository->billEndsWith($value, 1337); + $result = $this->billRepository->billEndsWith($value, 1337); if ($result->count() > 0) { $this->collector->excludeBills($result); } @@ -2076,7 +2076,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'bill_starts': - $result = $this->billRepository->billStartsWith($value, 1337); + $result = $this->billRepository->billStartsWith($value, 1337); if ($result->count() > 0) { $this->collector->setBills($result); } @@ -2088,7 +2088,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-bill_starts': - $result = $this->billRepository->billStartsWith($value, 1337); + $result = $this->billRepository->billStartsWith($value, 1337); if ($result->count() > 0) { $this->collector->excludeBills($result); } @@ -2099,9 +2099,9 @@ class OperatorQuerySearch implements SearchInterface break; - // - // tags - // + // + // tags + // case '-has_any_tag': case 'has_no_tag': $this->collector->withoutTags(); @@ -2116,7 +2116,7 @@ class OperatorQuerySearch implements SearchInterface case '-tag_is_not': case 'tag_is': - $result = $this->tagRepository->findByTag($value); + $result = $this->tagRepository->findByTag($value); if (null !== $result) { $this->includeTags[] = $result->id; $this->includeTags = array_unique($this->includeTags); @@ -2131,7 +2131,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'tag_contains': - $tags = $this->tagRepository->searchTag($value); + $tags = $this->tagRepository->searchTag($value); if (0 === $tags->count()) { Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); Log::warning(sprintf('Call to findNothing() from %s.', $operator)); @@ -2146,7 +2146,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'tag_starts': - $tags = $this->tagRepository->tagStartsWith($value); + $tags = $this->tagRepository->tagStartsWith($value); if (0 === $tags->count()) { Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); Log::warning(sprintf('Call to findNothing() from %s.', $operator)); @@ -2161,7 +2161,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-tag_starts': - $tags = $this->tagRepository->tagStartsWith($value); + $tags = $this->tagRepository->tagStartsWith($value); if (0 === $tags->count()) { Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); Log::warning(sprintf('Call to findNothing() from %s.', $operator)); @@ -2175,7 +2175,7 @@ class OperatorQuerySearch implements SearchInterface break; case 'tag_ends': - $tags = $this->tagRepository->tagEndsWith($value); + $tags = $this->tagRepository->tagEndsWith($value); if (0 === $tags->count()) { Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); Log::warning(sprintf('Call to findNothing() from %s.', $operator)); @@ -2189,7 +2189,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-tag_ends': - $tags = $this->tagRepository->tagEndsWith($value); + $tags = $this->tagRepository->tagEndsWith($value); if (0 === $tags->count()) { Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); Log::warning(sprintf('Call to findNothing() from %s.', $operator)); @@ -2203,7 +2203,7 @@ class OperatorQuerySearch implements SearchInterface break; case '-tag_contains': - $tags = $this->tagRepository->searchTag($value)->keyBy('id'); + $tags = $this->tagRepository->searchTag($value)->keyBy('id'); if (0 === $tags->count()) { Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); @@ -2219,7 +2219,7 @@ class OperatorQuerySearch implements SearchInterface case '-tag_is': case 'tag_is_not': - $result = $this->tagRepository->findByTag($value); + $result = $this->tagRepository->findByTag($value); if (null !== $result) { $this->excludeTags[] = $result->id; $this->excludeTags = array_unique($this->excludeTags); @@ -2227,9 +2227,9 @@ class OperatorQuerySearch implements SearchInterface break; - // - // notes - // + // + // notes + // case 'notes_contains': $this->collector->notesContain($value); @@ -2292,14 +2292,14 @@ class OperatorQuerySearch implements SearchInterface break; - // - // amount - // + // + // amount + // case 'amount_is': // strip comma's, make dots. Log::debug(sprintf('Original value "%s"', $value)); - $value = str_replace(',', '.', $value); - $amount = app('steam')->positive($value); + $value = str_replace(',', '.', $value); + $amount = app('steam')->positive($value); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->amountIs($amount); @@ -2308,8 +2308,8 @@ class OperatorQuerySearch implements SearchInterface case '-amount_is': // strip comma's, make dots. Log::debug(sprintf('Original value "%s"', $value)); - $value = str_replace(',', '.', $value); - $amount = app('steam')->positive($value); + $value = str_replace(',', '.', $value); + $amount = app('steam')->positive($value); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->amountIsNot($amount); @@ -2317,9 +2317,9 @@ class OperatorQuerySearch implements SearchInterface case 'foreign_amount_is': // strip comma's, make dots. - $value = str_replace(',', '.', $value); + $value = str_replace(',', '.', $value); - $amount = app('steam')->positive($value); + $amount = app('steam')->positive($value); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->foreignAmountIs($amount); @@ -2327,9 +2327,9 @@ class OperatorQuerySearch implements SearchInterface case '-foreign_amount_is': // strip comma's, make dots. - $value = str_replace(',', '.', $value); + $value = str_replace(',', '.', $value); - $amount = app('steam')->positive($value); + $amount = app('steam')->positive($value); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->foreignAmountIsNot($amount); @@ -2338,9 +2338,9 @@ class OperatorQuerySearch implements SearchInterface case '-amount_more': case 'amount_less': // strip comma's, make dots. - $value = str_replace(',', '.', $value); + $value = str_replace(',', '.', $value); - $amount = app('steam')->positive($value); + $amount = app('steam')->positive($value); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->amountLess($amount); @@ -2349,9 +2349,9 @@ class OperatorQuerySearch implements SearchInterface case '-foreign_amount_more': case 'foreign_amount_less': // strip comma's, make dots. - $value = str_replace(',', '.', $value); + $value = str_replace(',', '.', $value); - $amount = app('steam')->positive($value); + $amount = app('steam')->positive($value); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->foreignAmountLess($amount); @@ -2361,8 +2361,8 @@ class OperatorQuerySearch implements SearchInterface case 'amount_more': Log::debug(sprintf('Now handling operator "%s"', $operator)); // strip comma's, make dots. - $value = str_replace(',', '.', $value); - $amount = app('steam')->positive($value); + $value = str_replace(',', '.', $value); + $amount = app('steam')->positive($value); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->amountMore($amount); @@ -2372,16 +2372,16 @@ class OperatorQuerySearch implements SearchInterface case 'foreign_amount_more': Log::debug(sprintf('Now handling operator "%s"', $operator)); // strip comma's, make dots. - $value = str_replace(',', '.', $value); - $amount = app('steam')->positive($value); + $value = str_replace(',', '.', $value); + $amount = app('steam')->positive($value); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->foreignAmountMore($amount); break; - // - // transaction type - // + // + // transaction type + // case 'transaction_type': $this->collector->setTypes([ucfirst($value)]); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); @@ -2394,152 +2394,152 @@ class OperatorQuerySearch implements SearchInterface break; - // - // dates - // + // + // dates + // case '-date_on': case 'date_on': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setExactDateParams($range, $prohibited); return false; case 'date_before': case '-date_after': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setDateBeforeParams($range); return false; case 'date_after': case '-date_before': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setDateAfterParams($range); return false; case 'interest_date_on': case '-interest_date_on': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setExactMetaDateParams('interest_date', $range, $prohibited); return false; case 'interest_date_before': case '-interest_date_after': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateBeforeParams('interest_date', $range); return false; case 'interest_date_after': case '-interest_date_before': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateAfterParams('interest_date', $range); return false; case 'book_date_on': case '-book_date_on': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setExactMetaDateParams('book_date', $range, $prohibited); return false; case 'book_date_before': case '-book_date_after': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateBeforeParams('book_date', $range); return false; case 'book_date_after': case '-book_date_before': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateAfterParams('book_date', $range); return false; case 'process_date_on': case '-process_date_on': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setExactMetaDateParams('process_date', $range, $prohibited); return false; case 'process_date_before': case '-process_date_after': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateBeforeParams('process_date', $range); return false; case 'process_date_after': case '-process_date_before': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateAfterParams('process_date', $range); return false; case 'due_date_on': case '-due_date_on': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setExactMetaDateParams('due_date', $range, $prohibited); return false; case 'due_date_before': case '-due_date_after': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateBeforeParams('due_date', $range); return false; case 'due_date_after': case '-due_date_before': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateAfterParams('due_date', $range); return false; case 'payment_date_on': case '-payment_date_on': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setExactMetaDateParams('payment_date', $range, $prohibited); return false; case 'payment_date_before': case '-payment_date_after': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateBeforeParams('payment_date', $range); return false; case 'payment_date_after': case '-payment_date_before': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateAfterParams('payment_date', $range); return false; case 'invoice_date_on': case '-invoice_date_on': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setExactMetaDateParams('invoice_date', $range, $prohibited); return false; case 'invoice_date_before': case '-invoice_date_after': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateBeforeParams('invoice_date', $range); return false; case 'invoice_date_after': case '-invoice_date_before': - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setMetaDateAfterParams('invoice_date', $range); return false; @@ -2547,7 +2547,7 @@ class OperatorQuerySearch implements SearchInterface case 'created_at_on': case '-created_at_on': Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setExactObjectDateParams('created_at', $range, $prohibited); return false; @@ -2555,7 +2555,7 @@ class OperatorQuerySearch implements SearchInterface case 'created_at_before': case '-created_at_after': Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setObjectDateBeforeParams('created_at', $range); return false; @@ -2563,7 +2563,7 @@ class OperatorQuerySearch implements SearchInterface case 'created_at_after': case '-created_at_before': Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setObjectDateAfterParams('created_at', $range); return false; @@ -2571,7 +2571,7 @@ class OperatorQuerySearch implements SearchInterface case 'updated_at_on': case '-updated_at_on': Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setExactObjectDateParams('updated_at', $range, $prohibited); return false; @@ -2579,7 +2579,7 @@ class OperatorQuerySearch implements SearchInterface case 'updated_at_before': case '-updated_at_after': Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setObjectDateBeforeParams('updated_at', $range); return false; @@ -2587,14 +2587,14 @@ class OperatorQuerySearch implements SearchInterface case 'updated_at_after': case '-updated_at_before': Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); - $range = $this->parseDateRange($operator, $value); + $range = $this->parseDateRange($operator, $value); $this->setObjectDateAfterParams('updated_at', $range); return false; - // - // external URL - // + // + // external URL + // case '-any_external_url': case 'no_external_url': $this->collector->withoutExternalUrl(); @@ -2659,9 +2659,9 @@ class OperatorQuerySearch implements SearchInterface break; - // - // other fields - // + // + // other fields + // case 'external_id_is': $this->collector->setExternalId($value); diff --git a/app/Support/Search/QueryParser/GdbotsQueryParser.php b/app/Support/Search/QueryParser/GdbotsQueryParser.php index 0e670a21df..e532901696 100644 --- a/app/Support/Search/QueryParser/GdbotsQueryParser.php +++ b/app/Support/Search/QueryParser/GdbotsQueryParser.php @@ -32,6 +32,7 @@ use Gdbots\QueryParser\QueryParser as BaseQueryParser; use Illuminate\Support\Facades\Log; use LogicException; use TypeError; + use function Safe\fwrite; class GdbotsQueryParser implements QueryParserInterface @@ -51,12 +52,12 @@ class GdbotsQueryParser implements QueryParserInterface try { $result = $this->parser->parse($query); $nodes = array_map( - fn(GdbotsNode\Node $node) => $this->convertNode($node), + fn (GdbotsNode\Node $node) => $this->convertNode($node), $result->getNodes() ); return new NodeGroup($nodes); - } catch (LogicException | TypeError $e) { + } catch (LogicException|TypeError $e) { fwrite(STDERR, "Setting up GdbotsQueryParserTest\n"); app('log')->error($e->getMessage()); app('log')->error(sprintf('Could not parse search: "%s".', $query)); @@ -84,7 +85,7 @@ class GdbotsQueryParser implements QueryParserInterface return new NodeGroup( array_map( - fn(GdbotsNode\Node $subNode) => $this->convertNode($subNode), + fn (GdbotsNode\Node $subNode) => $this->convertNode($subNode), $node->getNodes() ) ); diff --git a/app/Support/Search/QueryParser/QueryParser.php b/app/Support/Search/QueryParser/QueryParser.php index 2533bcc3a8..bef27a0ec9 100644 --- a/app/Support/Search/QueryParser/QueryParser.php +++ b/app/Support/Search/QueryParser/QueryParser.php @@ -139,7 +139,7 @@ class QueryParser implements QueryParserInterface if ('' === $tokenUnderConstruction) { // In any other location, it's just a normal character $tokenUnderConstruction .= $char; - $skipNext = true; + $skipNext = true; } if ('' !== $tokenUnderConstruction && !$skipNext) { // @phpstan-ignore-line Log::debug(sprintf('Turns out that "%s" is a field name. Reset the token.', $tokenUnderConstruction)); @@ -171,7 +171,7 @@ class QueryParser implements QueryParserInterface ++$this->position; } - $finalNode = '' !== $tokenUnderConstruction || '' !== $fieldName + $finalNode = '' !== $tokenUnderConstruction || '' !== $fieldName ? $this->createNode($tokenUnderConstruction, $fieldName, $prohibited) : null; @@ -184,7 +184,7 @@ class QueryParser implements QueryParserInterface $nodeResult = $this->buildNextNode($isSubquery); while ($nodeResult->node instanceof Node) { - $nodes[] = $nodeResult->node; + $nodes[] = $nodeResult->node; if ($nodeResult->isSubqueryEnd) { break; } diff --git a/app/Support/Singleton/PreferencesSingleton.php b/app/Support/Singleton/PreferencesSingleton.php index e8ff779c5e..287a964361 100644 --- a/app/Support/Singleton/PreferencesSingleton.php +++ b/app/Support/Singleton/PreferencesSingleton.php @@ -29,7 +29,7 @@ class PreferencesSingleton { private static ?PreferencesSingleton $instance = null; - private array $preferences = []; + private array $preferences = []; private function __construct() { diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 32b7c160bd..46119a268a 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -38,6 +38,7 @@ use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; use ValueError; + use function Safe\parse_url; use function Safe\preg_replace; @@ -49,45 +50,46 @@ class Steam public function accountsBalancesOptimized(Collection $accounts, Carbon $date, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null): array { Log::debug(sprintf('accountsBalancesOptimized: Called for %d account(s) with date/time "%s"', $accounts->count(), $date->toIso8601String())); - $result = []; + $result = []; $convertToPrimary ??= Amount::convertToPrimary(); $primary ??= Amount::getPrimaryCurrency(); - $currencies = $this->getCurrencies($accounts); + $currencies = $this->getCurrencies($accounts); // balance(s) in all currencies for ALL accounts. $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')) - ->groupBy(['transactions.account_id', 'transaction_currencies.code']) - ->get(['transactions.account_id', 'transaction_currencies.code', DB::raw('SUM(transactions.amount) as sum_of_amount')])->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')) + ->groupBy(['transactions.account_id', 'transaction_currencies.code']) + ->get(['transactions.account_id', 'transaction_currencies.code', DB::raw('SUM(transactions.amount) as sum_of_amount')])->toArray() + ; /** @var Account $account */ foreach ($accounts as $account) { // this array is PER account, so we wait a bit before we change code here. - $return = [ + $return = [ 'pc_balance' => '0', 'balance' => '0', // this key is overwritten right away, but I must remember it is always created. ]; - $currency = $currencies[$account->id]; + $currency = $currencies[$account->id]; // second array - $accountSum = array_filter($arrayOfSums, fn($entry) => $entry['account_id'] === $account->id); + $accountSum = array_filter($arrayOfSums, fn ($entry) => $entry['account_id'] === $account->id); if (0 === count($accountSum)) { $result[$account->id] = $return; continue; } - $accountSum = array_values($accountSum)[0]; - $sumOfAmount = (string)$accountSum['sum_of_amount']; - $sumOfAmount = $this->floatalize('' === $sumOfAmount ? '0' : $sumOfAmount); - $sumsByCode = [ + $accountSum = array_values($accountSum)[0]; + $sumOfAmount = (string)$accountSum['sum_of_amount']; + $sumOfAmount = $this->floatalize('' === $sumOfAmount ? '0' : $sumOfAmount); + $sumsByCode = [ $accountSum['code'] => $sumOfAmount, ]; // Log::debug('All balances are (joined)', $others); // if there is no request to convert, take this as "balance" and "pc_balance". - $return['balance'] = $sumsByCode[$currency->code] ?? '0'; + $return['balance'] = $sumsByCode[$currency->code] ?? '0'; if (!$convertToPrimary) { unset($return['pc_balance']); // Log::debug(sprintf('Set balance to %s, unset pc_balance', $return['balance'])); @@ -99,7 +101,7 @@ class Steam } // either way, the balance is always combined with the virtual balance: - $virtualBalance = (string)('' === (string)$account->virtual_balance ? '0' : $account->virtual_balance); + $virtualBalance = (string)('' === (string)$account->virtual_balance ? '0' : $account->virtual_balance); if ($convertToPrimary) { // the primary currency balance is combined with a converted virtual_balance: @@ -140,10 +142,10 @@ class Steam // Log::debug(sprintf('Trying bcround("%s",%d)', $number, $precision)); if (str_contains($number, '.')) { if ('-' !== $number[0]) { - return bcadd($number, '0.' . str_repeat('0', $precision) . '5', $precision); + return bcadd($number, '0.'.str_repeat('0', $precision).'5', $precision); } - return bcsub($number, '0.' . str_repeat('0', $precision) . '5', $precision); + return bcsub($number, '0.'.str_repeat('0', $precision).'5', $precision); } return $number; @@ -287,7 +289,7 @@ class Steam public function finalAccountBalance(Account $account, Carbon $date, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null): array { - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($account->id); $cache->addProperty($date); if ($cache->has()) { @@ -303,7 +305,7 @@ class Steam $primary = Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup); } // account balance thing. - $currencyPresent = isset($account->meta) && array_key_exists('currency', $account->meta) && null !== $account->meta['currency']; + $currencyPresent = isset($account->meta) && array_key_exists('currency', $account->meta) && null !== $account->meta['currency']; if ($currencyPresent) { $accountCurrency = $account->meta['currency']; } @@ -311,19 +313,20 @@ class Steam $accountCurrency = $this->getAccountCurrency($account); } - $hasCurrency = null !== $accountCurrency; - $currency = $hasCurrency ? $accountCurrency : $primary; - $return = [ + $hasCurrency = null !== $accountCurrency; + $currency = $hasCurrency ? $accountCurrency : $primary; + $return = [ 'pc_balance' => '0', 'balance' => '0', // this key is overwritten right away, but I must remember it is always created. ]; // balance(s) in all currencies. - $array = $account->transactions() - ->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')) - ->get(['transaction_currencies.code', 'transactions.amount'])->toArray(); - $others = $this->groupAndSumTransactions($array, 'code', 'amount'); + $array = $account->transactions() + ->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')) + ->get(['transaction_currencies.code', 'transactions.amount'])->toArray() + ; + $others = $this->groupAndSumTransactions($array, 'code', 'amount'); // Log::debug('All balances are (joined)', $others); // if there is no request to convert, take this as "balance" and "pc_balance". $return['balance'] = $others[$currency->code] ?? '0'; @@ -338,7 +341,7 @@ class Steam } // either way, the balance is always combined with the virtual balance: - $virtualBalance = (string)('' === (string)$account->virtual_balance ? '0' : $account->virtual_balance); + $virtualBalance = (string)('' === (string)$account->virtual_balance ? '0' : $account->virtual_balance); if ($convertToPrimary) { // the primary currency balance is combined with a converted virtual_balance: @@ -352,7 +355,7 @@ class Steam $return['balance'] = bcadd($return['balance'], $virtualBalance); // Log::debug(sprintf('Virtual balance makes the (primary currency) total %s', $return['balance'])); } - $final = array_merge($return, $others); + $final = array_merge($return, $others); // Log::debug('Final balance is', $final); $cache->store($final); @@ -367,7 +370,7 @@ class Steam Log::debug(sprintf('finalAccountBalanceInRange(#%d, %s, %s)', $account->id, $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); // set up cache - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($account->id); $cache->addProperty('final-balance-in-range'); $cache->addProperty($start); @@ -377,22 +380,22 @@ class Steam return $cache->get(); } - $balances = []; - $formatted = $start->format('Y-m-d'); + $balances = []; + $formatted = $start->format('Y-m-d'); /* * To make sure the start balance is correct, we need to get the balance at the exact end of the previous day. * Since we just did "startOfDay" we can do subDay()->endOfDay() to get the correct moment. * THAT will be the start balance. */ - $request = clone $start; + $request = clone $start; $request->subDay()->endOfDay(); Log::debug('Get first balance to start.'); Log::debug(sprintf('finalAccountBalanceInRange: Call finalAccountBalance with date/time "%s"', $request->toIso8601String())); - $startBalance = $this->finalAccountBalance($account, $request); - $primaryCurrency = Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup); - $accountCurrency = $this->getAccountCurrency($account); - $hasCurrency = $accountCurrency instanceof TransactionCurrency; - $currency = $accountCurrency ?? $primaryCurrency; + $startBalance = $this->finalAccountBalance($account, $request); + $primaryCurrency = Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup); + $accountCurrency = $this->getAccountCurrency($account); + $hasCurrency = $accountCurrency instanceof TransactionCurrency; + $currency = $accountCurrency ?? $primaryCurrency; Log::debug(sprintf('Currency is %s', $currency->code)); @@ -405,7 +408,7 @@ class Steam Log::debug(sprintf('Also set start balance in %s', $primaryCurrency->code)); $startBalance[$primaryCurrency->code] ??= '0'; } - $currencies = [ + $currencies = [ $currency->id => $currency, $primaryCurrency->id => $primaryCurrency, ]; @@ -415,47 +418,48 @@ class Steam // sums up the balance changes per day. Log::debug(sprintf('Date >= %s and <= %s', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); - $set = $account->transactions() - ->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->where('transaction_journals.date', '>=', $start->format('Y-m-d H:i:s')) - ->where('transaction_journals.date', '<=', $end->format('Y-m-d H:i:s')) - ->groupBy('transaction_journals.date') - ->groupBy('transactions.transaction_currency_id') - ->orderBy('transaction_journals.date', 'ASC') - ->whereNull('transaction_journals.deleted_at') - ->get( - [ // @phpstan-ignore-line - 'transaction_journals.date', - 'transactions.transaction_currency_id', - DB::raw('SUM(transactions.amount) AS sum_of_day'), - ] - ); + $set = $account->transactions() + ->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->where('transaction_journals.date', '>=', $start->format('Y-m-d H:i:s')) + ->where('transaction_journals.date', '<=', $end->format('Y-m-d H:i:s')) + ->groupBy('transaction_journals.date') + ->groupBy('transactions.transaction_currency_id') + ->orderBy('transaction_journals.date', 'ASC') + ->whereNull('transaction_journals.deleted_at') + ->get( + [ // @phpstan-ignore-line + 'transaction_journals.date', + 'transactions.transaction_currency_id', + DB::raw('SUM(transactions.amount) AS sum_of_day'), + ] + ) + ; - $currentBalance = $startBalance; - $converter = new ExchangeRateConverter(); + $currentBalance = $startBalance; + $converter = new ExchangeRateConverter(); /** @var Transaction $entry */ foreach ($set as $entry) { // get date object - $carbon = new Carbon($entry->date, $entry->date_tz); - $carbonKey = $carbon->format('Y-m-d'); + $carbon = new Carbon($entry->date, $entry->date_tz); + $carbonKey = $carbon->format('Y-m-d'); // make sure sum is a string: - $sumOfDay = (string)($entry->sum_of_day ?? '0'); + $sumOfDay = (string)($entry->sum_of_day ?? '0'); // #10426 make sure sum is not in scientific notation. - $sumOfDay = $this->floatalize($sumOfDay); + $sumOfDay = $this->floatalize($sumOfDay); // find currency of this entry, does not have to exist. $currencies[$entry->transaction_currency_id] ??= Amount::getTransactionCurrencyById($entry->transaction_currency_id); // make sure this $entry has its own $entryCurrency /** @var TransactionCurrency $entryCurrency */ - $entryCurrency = $currencies[$entry->transaction_currency_id]; + $entryCurrency = $currencies[$entry->transaction_currency_id]; Log::debug(sprintf('Processing transaction(s) on moment %s', $carbon->format('Y-m-d H:i:s'))); // add amount to current balance in currency code. - $currentBalance[$entryCurrency->code] ??= '0'; + $currentBalance[$entryCurrency->code] ??= '0'; $currentBalance[$entryCurrency->code] = bcadd($sumOfDay, (string)$currentBalance[$entryCurrency->code]); // if not requested to convert to primary currency, add the amount to "balance", do nothing else. @@ -473,7 +477,7 @@ class Steam } } // add to final array. - $balances[$carbonKey] = $currentBalance; + $balances[$carbonKey] = $currentBalance; Log::debug(sprintf('Updated entry [%s]', $carbonKey), $currentBalance); } $cache->store($balances); @@ -490,7 +494,7 @@ class Steam */ public function floatalize(string $value): string { - $value = strtoupper($value); + $value = strtoupper($value); if (!str_contains($value, 'E')) { return $value; } @@ -514,8 +518,8 @@ class Steam public function getAccountCurrency(Account $account): ?TransactionCurrency { - $type = $account->accountType->type; - $list = config('firefly.valid_currency_account_types'); + $type = $account->accountType->type; + $list = config('firefly.valid_currency_account_types'); // return null if not in this list. if (!in_array($type, $list, true)) { @@ -569,15 +573,15 @@ class Steam { $list = []; - $set = auth()->user()->transactions() - ->whereIn('transactions.account_id', $accounts) - ->groupBy(['transactions.account_id', 'transaction_journals.user_id']) - ->get(['transactions.account_id', DB::raw('MAX(transaction_journals.date) AS max_date')]) // @phpstan-ignore-line + $set = auth()->user()->transactions() + ->whereIn('transactions.account_id', $accounts) + ->groupBy(['transactions.account_id', 'transaction_journals.user_id']) + ->get(['transactions.account_id', DB::raw('MAX(transaction_journals.date) AS max_date')]) // @phpstan-ignore-line ; /** @var Transaction $entry */ foreach ($set as $entry) { - $date = new Carbon($entry->max_date, config('app.timezone')); + $date = new Carbon($entry->max_date, config('app.timezone')); $date->setTimezone(config('app.timezone')); $list[(int)$entry->account_id] = $date; } @@ -591,24 +595,25 @@ class Steam public function getLocale(): string // get preference { $singleton = PreferencesSingleton::getInstance(); - $cached = $singleton->getPreference('locale'); - if(null !== $cached) { + $cached = $singleton->getPreference('locale'); + if (null !== $cached) { return $cached; } - $locale = app('preferences')->get('locale', config('firefly.default_locale', 'equal'))->data; + $locale = app('preferences')->get('locale', config('firefly.default_locale', 'equal'))->data; if (is_array($locale)) { $locale = 'equal'; } if ('equal' === $locale) { $locale = $this->getLanguage(); } - $locale = (string)$locale; + $locale = (string)$locale; // Check for Windows to replace the locale correctly. if ('WIN' === strtoupper(substr(PHP_OS, 0, 3))) { $locale = str_replace('_', '-', $locale); } $singleton->setPreference('locale', $locale); + return $locale; } @@ -642,9 +647,9 @@ class Steam public function getSafeUrl(string $unknownUrl, string $safeUrl): string { // Log::debug(sprintf('getSafeUrl(%s, %s)', $unknownUrl, $safeUrl)); - $returnUrl = $safeUrl; - $unknownHost = parse_url($unknownUrl, PHP_URL_HOST); - $safeHost = parse_url($safeUrl, PHP_URL_HOST); + $returnUrl = $safeUrl; + $unknownHost = parse_url($unknownUrl, PHP_URL_HOST); + $safeHost = parse_url($safeUrl, PHP_URL_HOST); if (null !== $unknownHost && $unknownHost === $safeHost) { $returnUrl = $unknownUrl; @@ -746,12 +751,12 @@ class Steam if (null === $preference) { $singleton->setPreference($key, $currency); } - $current = $amount; + $current = $amount; if ($currency->id !== $primary->id) { $current = $converter->convert($currency, $primary, $date, $amount); Log::debug(sprintf('Convert %s %s to %s %s', $currency->code, $amount, $primary->code, $current)); } - $total = bcadd($current, $total); + $total = bcadd($current, $total); } return $total; @@ -765,8 +770,8 @@ class Steam $primary = Amount::getPrimaryCurrency(); $currencies[$primary->id] = $primary; - $ids = $accounts->pluck('id')->toArray(); - $result = AccountMeta::whereIn('account_id', $ids)->where('name', 'currency_id')->get(); + $ids = $accounts->pluck('id')->toArray(); + $result = AccountMeta::whereIn('account_id', $ids)->where('name', 'currency_id')->get(); /** @var AccountMeta $item */ foreach ($result as $item) { @@ -776,7 +781,7 @@ class Steam } } // collect those currencies, skip primary because we already have it. - $set = TransactionCurrency::whereIn('id', $accountPreferences)->where('id', '!=', $primary->id)->get(); + $set = TransactionCurrency::whereIn('id', $accountPreferences)->where('id', '!=', $primary->id)->get(); foreach ($set as $item) { $currencies[$item->id] = $item; } @@ -787,7 +792,7 @@ class Steam $currencyPresent = isset($account->meta) && array_key_exists('currency', $account->meta) && null !== $account->meta['currency']; if ($currencyPresent) { $currencyId = $account->meta['currency']->id; - $currencies[$currencyId] ??= $account->meta['currency']; + $currencies[$currencyId] ??= $account->meta['currency']; $accountCurrencies[$accountId] = $account->meta['currency']; } if (!$currencyPresent && !array_key_exists($accountId, $accountPreferences)) { diff --git a/app/Support/System/OAuthKeys.php b/app/Support/System/OAuthKeys.php index 1c1f2276cf..97bd74bd19 100644 --- a/app/Support/System/OAuthKeys.php +++ b/app/Support/System/OAuthKeys.php @@ -31,6 +31,7 @@ use Illuminate\Support\Facades\Crypt; use Laravel\Passport\Console\KeysCommand; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; + use function Safe\file_get_contents; use function Safe\file_put_contents; @@ -65,7 +66,7 @@ class OAuthKeys try { $privateKey = (string)app('fireflyconfig')->get(self::PRIVATE_KEY)?->data; $publicKey = (string)app('fireflyconfig')->get(self::PUBLIC_KEY)?->data; - } catch (ContainerExceptionInterface | FireflyException | NotFoundExceptionInterface $e) { + } catch (ContainerExceptionInterface|FireflyException|NotFoundExceptionInterface $e) { app('log')->error(sprintf('Could not validate keysInDatabase(): %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); } @@ -98,8 +99,8 @@ class OAuthKeys return false; } - $private = storage_path('oauth-private.key'); - $public = storage_path('oauth-public.key'); + $private = storage_path('oauth-private.key'); + $public = storage_path('oauth-public.key'); file_put_contents($private, $privateContent); file_put_contents($public, $publicContent); diff --git a/app/Support/Twig/AmountFormat.php b/app/Support/Twig/AmountFormat.php index 49c9a6d11e..dbf22254b3 100644 --- a/app/Support/Twig/AmountFormat.php +++ b/app/Support/Twig/AmountFormat.php @@ -145,13 +145,13 @@ class AmountFormat extends AbstractExtension static function (string $amount, ?string $symbol = null, ?int $decimalPlaces = null, ?bool $coloured = null): string { if (null === $symbol) { - $message = sprintf('formatAmountBySymbol("%s", %s, %d, %s) was called without a symbol. Please browse to /flush to clear your cache.', $amount, var_export($symbol, true), $decimalPlaces, var_export($coloured, true)); + $message = sprintf('formatAmountBySymbol("%s", %s, %d, %s) was called without a symbol. Please browse to /flush to clear your cache.', $amount, var_export($symbol, true), $decimalPlaces, var_export($coloured, true)); Log::error($message); $currency = Amount::getPrimaryCurrency(); } if (null !== $symbol) { - $decimalPlaces ??= 2; - $coloured ??= true; + $decimalPlaces ??= 2; + $coloured ??= true; $currency = new TransactionCurrency(); $currency->symbol = $symbol; $currency->decimal_places = $decimalPlaces; diff --git a/app/Support/Twig/General.php b/app/Support/Twig/General.php index 337e832312..485dfe1bd9 100644 --- a/app/Support/Twig/General.php +++ b/app/Support/Twig/General.php @@ -37,6 +37,7 @@ use Override; use Twig\Extension\AbstractExtension; use Twig\TwigFilter; use Twig\TwigFunction; + use function Safe\parse_url; /** @@ -104,7 +105,7 @@ class General extends AbstractExtension 'activeRoutePartialObjectType', static function ($context): string { [, $route, $objectType] = func_get_args(); - $activeObjectType = $context['objectType'] ?? false; + $activeObjectType = $context['objectType'] ?? false; if ($objectType === $activeObjectType && false !== stripos( @@ -154,14 +155,14 @@ class General extends AbstractExtension } /** @var Carbon $date */ - $date = session('end', today(config('app.timezone'))->endOfMonth()); + $date = session('end', today(config('app.timezone'))->endOfMonth()); Log::debug(sprintf('twig balance: Call finalAccountBalance with date/time "%s"', $date->toIso8601String())); $info = Steam::finalAccountBalance($account, $date); $currency = Steam::getAccountCurrency($account); $primary = Amount::getPrimaryCurrency(); $convertToPrimary = Amount::convertToPrimary(); $usePrimary = $convertToPrimary && $primary->id !== $currency->id; - $currency ??= $primary; + $currency ??= $primary; $strings = []; foreach ($info as $key => $balance) { if ('balance' === $key) { @@ -196,7 +197,7 @@ class General extends AbstractExtension { return new TwigFunction( 'carbonize', - static fn(string $date): Carbon => new Carbon($date, config('app.timezone')) + static fn (string $date): Carbon => new Carbon($date, config('app.timezone')) ); } @@ -225,15 +226,15 @@ class General extends AbstractExtension static function (int $size): string { // less than one GB, more than one MB if ($size < (1024 * 1024 * 2014) && $size >= (1024 * 1024)) { - return round($size / (1024 * 1024), 2) . ' MB'; + return round($size / (1024 * 1024), 2).' MB'; } // less than one MB if ($size < (1024 * 1024)) { - return round($size / 1024, 2) . ' KB'; + return round($size / 1024, 2).' KB'; } - return $size . ' bytes'; + return $size.' bytes'; } ); } @@ -337,7 +338,7 @@ class General extends AbstractExtension { return new TwigFilter( 'mimeIcon', - static fn(string $string): string => match ($string) { + static fn (string $string): string => match ($string) { 'application/pdf' => 'fa-file-pdf-o', 'image/webp', 'image/png', 'image/jpeg', 'image/svg+xml', 'image/heic', 'image/heic-sequence', 'application/vnd.oasis.opendocument.image' => 'fa-file-image-o', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 'application/x-iwork-pages-sffpages', 'application/vnd.sun.xml.writer', 'application/vnd.sun.xml.writer.template', 'application/vnd.sun.xml.writer.global', 'application/vnd.stardivision.writer', 'application/vnd.stardivision.writer-global', 'application/vnd.oasis.opendocument.text', 'application/vnd.oasis.opendocument.text-template', 'application/vnd.oasis.opendocument.text-web', 'application/vnd.oasis.opendocument.text-master' => 'fa-file-word-o', @@ -380,7 +381,7 @@ class General extends AbstractExtension { return new TwigFunction( 'phpdate', - static fn(string $str): string => date($str) + static fn (string $str): string => date($str) ); } } diff --git a/app/Support/Twig/Rule.php b/app/Support/Twig/Rule.php index 7ed872df8b..ea60a67a3e 100644 --- a/app/Support/Twig/Rule.php +++ b/app/Support/Twig/Rule.php @@ -42,7 +42,7 @@ class Rule extends AbstractExtension $ruleActions = array_keys(Config::get('firefly.rule-actions')); $possibleActions = []; foreach ($ruleActions as $key) { - $possibleActions[$key] = (string)trans('firefly.rule_action_' . $key . '_choice'); + $possibleActions[$key] = (string)trans('firefly.rule_action_'.$key.'_choice'); } unset($ruleActions); asort($possibleActions); @@ -56,7 +56,7 @@ class Rule extends AbstractExtension { return new TwigFunction( 'allJournalTriggers', - static fn() => [ + static fn () => [ 'store-journal' => (string)trans('firefly.rule_trigger_store_journal'), 'update-journal' => (string)trans('firefly.rule_trigger_update_journal'), 'manual-activation' => (string)trans('firefly.rule_trigger_manual'), @@ -73,7 +73,7 @@ class Rule extends AbstractExtension $possibleTriggers = []; foreach ($ruleTriggers as $key) { if ('user_action' !== $key) { - $possibleTriggers[$key] = (string)trans('firefly.rule_trigger_' . $key . '_choice'); + $possibleTriggers[$key] = (string)trans('firefly.rule_trigger_'.$key.'_choice'); } } unset($ruleTriggers); diff --git a/app/Support/Twig/TransactionGroupTwig.php b/app/Support/Twig/TransactionGroupTwig.php index e2957ec3a1..3033a84872 100644 --- a/app/Support/Twig/TransactionGroupTwig.php +++ b/app/Support/Twig/TransactionGroupTwig.php @@ -34,6 +34,7 @@ use Illuminate\Support\Facades\DB; use Override; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; + use function Safe\json_decode; /** @@ -82,10 +83,11 @@ class TransactionGroupTwig extends AbstractExtension static function (int $journalId, string $metaField) { /** @var null|TransactionJournalMeta $entry */ $entry = DB::table('journal_meta') - ->where('name', $metaField) - ->where('transaction_journal_id', $journalId) - ->whereNull('deleted_at') - ->first(); + ->where('name', $metaField) + ->where('transaction_journal_id', $journalId) + ->whereNull('deleted_at') + ->first() + ; if (null === $entry) { return today(config('app.timezone')); } @@ -102,10 +104,11 @@ class TransactionGroupTwig extends AbstractExtension static function (int $journalId, string $metaField) { /** @var null|TransactionJournalMeta $entry */ $entry = DB::table('journal_meta') - ->where('name', $metaField) - ->where('transaction_journal_id', $journalId) - ->whereNull('deleted_at') - ->first(); + ->where('name', $metaField) + ->where('transaction_journal_id', $journalId) + ->whereNull('deleted_at') + ->first() + ; if (null === $entry) { return ''; } @@ -121,10 +124,11 @@ class TransactionGroupTwig extends AbstractExtension 'journalHasMeta', static function (int $journalId, string $metaField) { $count = DB::table('journal_meta') - ->where('name', $metaField) - ->where('transaction_journal_id', $journalId) - ->whereNull('deleted_at') - ->count(); + ->where('name', $metaField) + ->where('transaction_journal_id', $journalId) + ->whereNull('deleted_at') + ->count() + ; return 1 === $count; } @@ -157,9 +161,9 @@ class TransactionGroupTwig extends AbstractExtension */ private function foreignJournalArrayAmount(array $array): string { - $type = $array['transaction_type_type'] ?? TransactionTypeEnum::WITHDRAWAL->value; - $amount = $array['foreign_amount'] ?? '0'; - $colored = true; + $type = $array['transaction_type_type'] ?? TransactionTypeEnum::WITHDRAWAL->value; + $amount = $array['foreign_amount'] ?? '0'; + $colored = true; $sourceType = $array['source_account_type'] ?? 'invalid'; $amount = $this->signAmount($amount, $type, $sourceType); @@ -167,7 +171,7 @@ class TransactionGroupTwig extends AbstractExtension if (TransactionTypeEnum::TRANSFER->value === $type) { $colored = false; } - $result = app('amount')->formatFlat($array['foreign_currency_symbol'], (int)$array['foreign_currency_decimal_places'], $amount, $colored); + $result = app('amount')->formatFlat($array['foreign_currency_symbol'], (int)$array['foreign_currency_decimal_places'], $amount, $colored); if (TransactionTypeEnum::TRANSFER->value === $type) { return sprintf('%s', $result); } @@ -180,7 +184,7 @@ class TransactionGroupTwig extends AbstractExtension */ private function foreignJournalObjectAmount(TransactionJournal $journal): string { - $type = $journal->transactionType->type; + $type = $journal->transactionType->type; /** @var Transaction $first */ $first = $journal->transactions()->where('amount', '<', 0)->first(); @@ -189,12 +193,12 @@ class TransactionGroupTwig extends AbstractExtension $colored = true; $sourceType = $first->account->accountType()->first()->type; - $amount = $this->signAmount($amount, $type, $sourceType); + $amount = $this->signAmount($amount, $type, $sourceType); if (TransactionTypeEnum::TRANSFER->value === $type) { $colored = false; } - $result = app('amount')->formatFlat($currency->symbol, $currency->decimal_places, $amount, $colored); + $result = app('amount')->formatFlat($currency->symbol, $currency->decimal_places, $amount, $colored); if (TransactionTypeEnum::TRANSFER->value === $type) { return sprintf('%s', $result); } @@ -225,7 +229,7 @@ class TransactionGroupTwig extends AbstractExtension $colored = false; } - $result = app('amount')->formatFlat($array['currency_symbol'], (int)$array['currency_decimal_places'], $amount, $colored); + $result = app('amount')->formatFlat($array['currency_symbol'], (int)$array['currency_decimal_places'], $amount, $colored); if (TransactionTypeEnum::TRANSFER->value === $type) { return sprintf('%s', $result); } @@ -238,7 +242,7 @@ class TransactionGroupTwig extends AbstractExtension */ private function normalJournalObjectAmount(TransactionJournal $journal): string { - $type = $journal->transactionType->type; + $type = $journal->transactionType->type; /** @var Transaction $first */ $first = $journal->transactions()->where('amount', '<', 0)->first(); @@ -247,12 +251,12 @@ class TransactionGroupTwig extends AbstractExtension $colored = true; $sourceType = $first->account->accountType()->first()->type; - $amount = $this->signAmount($amount, $type, $sourceType); + $amount = $this->signAmount($amount, $type, $sourceType); if (TransactionTypeEnum::TRANSFER->value === $type) { $colored = false; } - $result = app('amount')->formatFlat($currency->symbol, $currency->decimal_places, $amount, $colored); + $result = app('amount')->formatFlat($currency->symbol, $currency->decimal_places, $amount, $colored); if (TransactionTypeEnum::TRANSFER->value === $type) { return sprintf('%s', $result); } diff --git a/app/Support/Twig/Translation.php b/app/Support/Twig/Translation.php index e4f429f07e..d316895ed7 100644 --- a/app/Support/Twig/Translation.php +++ b/app/Support/Twig/Translation.php @@ -39,7 +39,7 @@ class Translation extends AbstractExtension return [ new TwigFilter( '_', - static fn($name) => (string)trans(sprintf('firefly.%s', $name)), + static fn ($name) => (string)trans(sprintf('firefly.%s', $name)), ['is_safe' => ['html']] ), ]; diff --git a/composer.lock b/composer.lock index 18cb3ef987..f3717d8cb4 100644 --- a/composer.lock +++ b/composer.lock @@ -11511,21 +11511,21 @@ }, { "name": "phpstan/phpstan-strict-rules", - "version": "2.0.6", + "version": "2.0.7", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-strict-rules.git", - "reference": "f9f77efa9de31992a832ff77ea52eb42d675b094" + "reference": "d6211c46213d4181054b3d77b10a5c5cb0d59538" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/f9f77efa9de31992a832ff77ea52eb42d675b094", - "reference": "f9f77efa9de31992a832ff77ea52eb42d675b094", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/d6211c46213d4181054b3d77b10a5c5cb0d59538", + "reference": "d6211c46213d4181054b3d77b10a5c5cb0d59538", "shasum": "" }, "require": { "php": "^7.4 || ^8.0", - "phpstan/phpstan": "^2.0.4" + "phpstan/phpstan": "^2.1.29" }, "require-dev": { "php-parallel-lint/php-parallel-lint": "^1.2", @@ -11553,9 +11553,9 @@ "description": "Extra strict and opinionated rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", - "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.6" + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.7" }, - "time": "2025-07-21T12:19:29+00:00" + "time": "2025-09-26T11:19:08+00:00" }, { "name": "phpunit/php-code-coverage", diff --git a/config/firefly.php b/config/firefly.php index 3139bb0be9..cf0424ddc6 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -78,8 +78,8 @@ return [ 'running_balance_column' => env('USE_RUNNING_BALANCE', false), // see cer.php for exchange rates feature flag. ], - 'version' => 'develop/2025-09-25', - 'build_time' => 1758820163, + 'version' => 'develop/2025-09-26', + 'build_time' => 1758908498, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 27, diff --git a/package-lock.json b/package-lock.json index d69416dc1e..09bb10c0a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5736,9 +5736,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.223", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.223.tgz", - "integrity": "sha512-qKm55ic6nbEmagFlTFczML33rF90aU+WtrJ9MdTCThrcvDNdUHN4p6QfVN78U06ZmguqXIyMPyYhw2TrbDUwPQ==", + "version": "1.5.224", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.224.tgz", + "integrity": "sha512-kWAoUu/bwzvnhpdZSIc6KUyvkI1rbRXMT0Eq8pKReyOyaPZcctMli+EgvcN1PAvwVc7Tdo4Fxi2PsLNDU05mdg==", "dev": true, "license": "ISC" }, From 822dee6e70fd86035f211d47105a4b82d651e411 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 26 Sep 2025 19:48:20 +0200 Subject: [PATCH 47/91] Allow statistics to be removed from /flush --- app/Http/Controllers/Admin/UpdateController.php | 11 ++++++----- app/Http/Controllers/DebugController.php | 3 +++ app/Models/PeriodStatistic.php | 2 -- .../2025_09_25_175248_create_period_statistics.php | 1 - 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/Http/Controllers/Admin/UpdateController.php b/app/Http/Controllers/Admin/UpdateController.php index 8502d43ce8..90729abe56 100644 --- a/app/Http/Controllers/Admin/UpdateController.php +++ b/app/Http/Controllers/Admin/UpdateController.php @@ -27,6 +27,7 @@ use Carbon\Carbon; use FireflyIII\Helpers\Update\UpdateTrait; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Middleware\IsDemoUser; +use FireflyIII\Support\Facades\FireflyConfig; use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; @@ -66,8 +67,8 @@ class UpdateController extends Controller { $subTitle = (string) trans('firefly.update_check_title'); $subTitleIcon = 'fa-star'; - $permission = app('fireflyconfig')->get('permission_update_check', -1); - $channel = app('fireflyconfig')->get('update_channel', 'stable'); + $permission = FireflyConfig::get('permission_update_check', -1); + $channel = FireflyConfig::get('update_channel', 'stable'); $selected = $permission->data; $channelSelected = $channel->data; $options = [ @@ -96,9 +97,9 @@ class UpdateController extends Controller $channel = $request->get('update_channel'); $channel = in_array($channel, ['stable', 'beta', 'alpha'], true) ? $channel : 'stable'; - app('fireflyconfig')->set('permission_update_check', $checkForUpdates); - app('fireflyconfig')->set('last_update_check', Carbon::now()->getTimestamp()); - app('fireflyconfig')->set('update_channel', $channel); + FireflyConfig::set('permission_update_check', $checkForUpdates); + FireflyConfig::set('last_update_check', Carbon::now()->getTimestamp()); + FireflyConfig::set('update_channel', $channel); session()->flash('success', (string) trans('firefly.configuration_updated')); return redirect(route('settings.update-check')); diff --git a/app/Http/Controllers/DebugController.php b/app/Http/Controllers/DebugController.php index 73141d0b8c..f599788536 100644 --- a/app/Http/Controllers/DebugController.php +++ b/app/Http/Controllers/DebugController.php @@ -30,6 +30,7 @@ use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Middleware\IsDemoUser; +use FireflyIII\Models\PeriodStatistic; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Support\Facades\Amount; @@ -108,6 +109,8 @@ class DebugController extends Controller Artisan::call('route:clear'); Artisan::call('view:clear'); + PeriodStatistic::where('id','>',0)->delete(); + // also do some recalculations. Artisan::call('correction:recalculates-liabilities'); AccountBalanceCalculator::recalculateAll(false); diff --git a/app/Models/PeriodStatistic.php b/app/Models/PeriodStatistic.php index 194073bc88..519df3c79c 100644 --- a/app/Models/PeriodStatistic.php +++ b/app/Models/PeriodStatistic.php @@ -14,14 +14,12 @@ use Illuminate\Database\Eloquent\SoftDeletes; class PeriodStatistic extends Model { use ReturnsIntegerUserIdTrait; - use SoftDeletes; protected function casts(): array { return [ 'created_at' => 'datetime', 'updated_at' => 'datetime', - 'deleted_at' => 'datetime', 'start' => SeparateTimezoneCaster::class, 'end' => SeparateTimezoneCaster::class, ]; diff --git a/database/migrations/2025_09_25_175248_create_period_statistics.php b/database/migrations/2025_09_25_175248_create_period_statistics.php index e04afaa9de..0a5bf8d86b 100644 --- a/database/migrations/2025_09_25_175248_create_period_statistics.php +++ b/database/migrations/2025_09_25_175248_create_period_statistics.php @@ -14,7 +14,6 @@ return new class extends Migration Schema::create('period_statistics', function (Blueprint $table) { $table->id(); $table->timestamps(); - $table->softDeletes(); $table->integer('primary_statable_id', false, true)->nullable(); $table->string('primary_statable_type', 255)->nullable(); From 33dcce752510889801e2055d0d25cdea5f1c448f Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 26 Sep 2025 20:46:23 +0200 Subject: [PATCH 48/91] Optimize query for collecting transactions. --- .../Http/Controllers/PeriodOverview.php | 263 +++++++++--------- 1 file changed, 132 insertions(+), 131 deletions(-) diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index 6af3f30b6f..8e0d68f206 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -73,7 +73,8 @@ trait PeriodOverview protected AccountRepositoryInterface $accountRepository; protected JournalRepositoryInterface $journalRepos; protected PeriodStatisticRepositoryInterface $periodStatisticRepo; - private Collection $statistics; + private Collection $statistics; // temp data holder + private array $transactions; // temp data holder /** * This method returns "period entries", so nov-2015, dec-2015, etc. (this depends on the users session range) @@ -88,12 +89,12 @@ trait PeriodOverview $this->accountRepository = app(AccountRepositoryInterface::class); $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); $range = Navigation::getViewRange(true); - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); - $this->statistics = $this->periodStatisticRepo->allInRangeForModel($account, $start, $end); + $dates = Navigation::blockPeriods($start, $end, $range); + [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); + $this->statistics = $this->periodStatisticRepo->allInRangeForModel($account, $start, $end); // TODO needs to be re-arranged: // get all period stats for entire range. @@ -101,7 +102,7 @@ trait PeriodOverview // create new ones, or use collected. - $entries = []; + $entries = []; Log::debug(sprintf('Count of loops: %d', count($dates))); foreach ($dates as $currentDate) { $entries[] = $this->getSingleAccountPeriod($account, $currentDate['period'], $currentDate['start'], $currentDate['end']); @@ -137,11 +138,11 @@ trait PeriodOverview */ protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; // properties for entries with their amounts. - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty($range); @@ -153,32 +154,32 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setCategory($category); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setCategory($category); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setCategory($category); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); - $transferSet = $collector->getExtractedJournals(); + $transferSet = $collector->getExtractedJournals(); foreach ($dates as $currentDate) { $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); $earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']); @@ -186,17 +187,17 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'transactions' => 0, - 'title' => $title, - 'route' => route( - 'categories.show', - [$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] - ), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'transactions' => 0, + 'title' => $title, + 'route' => route( + 'categories.show', + [$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] + ), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } $cache->store($entries); @@ -212,11 +213,11 @@ trait PeriodOverview */ protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty($this->convertToPrimary); @@ -227,28 +228,28 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // get all expenses without a budget. /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end)->withoutBudget()->withAccountInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $journals = $collector->getExtractedJournals(); + $journals = $collector->getExtractedJournals(); foreach ($dates as $currentDate) { $set = $this->filterJournalsByDate($journals, $currentDate['start'], $currentDate['end']); $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'title' => $title, - 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($set), - 'spent' => $this->groupByCurrency($set), - 'earned' => [], - 'transferred_away' => [], - 'transferred_in' => [], - ]; + 'title' => $title, + 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($set), + 'spent' => $this->groupByCurrency($set), + 'earned' => [], + 'transferred_away' => [], + 'transferred_in' => [], + ]; } $cache->store($entries); @@ -265,38 +266,38 @@ trait PeriodOverview protected function getNoCategoryPeriodOverview(Carbon $theDate): array { Log::debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d'))); - $range = Navigation::getViewRange(true); - $first = $this->journalRepos->firstNull(); - $start = null === $first ? new Carbon() : $first->date; - $end = clone $theDate; - $end = Navigation::endOfPeriod($end, $range); + $range = Navigation::getViewRange(true); + $first = $this->journalRepos->firstNull(); + $start = null === $first ? new Carbon() : $first->date; + $end = clone $theDate; + $end = Navigation::endOfPeriod($end, $range); Log::debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d'))); Log::debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d'))); // properties for cache - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); @@ -310,13 +311,13 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'title' => $title, - 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'title' => $title, + 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } Log::debug('End of loops'); @@ -326,14 +327,15 @@ trait PeriodOverview protected function getSingleAccountPeriod(Account $account, string $period, Carbon $start, Carbon $end): array { Log::debug(sprintf('Now in getSingleAccountPeriod(#%d, %s %s)', $account->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); - $types = ['spent', 'earned', 'transferred_in', 'transferred_away']; - $return = [ + $types = ['spent', 'earned', 'transferred_in', 'transferred_away']; + $return = [ 'title' => Navigation::periodShow($start, $period), 'route' => route('accounts.show', [$account->id, $start->format('Y-m-d'), $start->format('Y-m-d')]), 'total_transactions' => 0, ]; + $this->transactions = []; foreach ($types as $type) { - $set = $this->getSingleAccountPeriodByType($account, $start, $end, $type); + $set = $this->getSingleAccountPeriodByType($account, $start, $end, $type); $return['total_transactions'] += $set['count']; unset($set['count']); $return[$type] = $set; @@ -371,49 +373,51 @@ trait PeriodOverview // nothing found, regenerate them. if (0 === $statistics->count()) { Log::debug(sprintf('Found nothing in this period for type "%s"', $type)); - $transactions = $this->accountRepository->periodCollection($account, $start, $end); + if (0 === count($this->transactions)) { + $this->transactions = $this->accountRepository->periodCollection($account, $start, $end); + } switch ($type) { default: throw new FireflyException(sprintf('Cannot deal with account period type %s', $type)); case 'spent': - $result = $this->filterTransactionsByType(TransactionTypeEnum::WITHDRAWAL, $transactions, $start, $end); + $result = $this->filterTransactionsByType(TransactionTypeEnum::WITHDRAWAL, $start, $end); break; case 'earned': - $result = $this->filterTransactionsByType(TransactionTypeEnum::DEPOSIT, $transactions, $start, $end); + $result = $this->filterTransactionsByType(TransactionTypeEnum::DEPOSIT, $start, $end); break; case 'transferred_in': - $result = $this->filterTransfers('in', $transactions, $start, $end); + $result = $this->filterTransfers('in', $start, $end); break; case 'transferred_away': - $result = $this->filterTransfers('away', $transactions, $start, $end); + $result = $this->filterTransfers('away', $start, $end); break; } // each result must be grouped by currency, then saved as period statistic. Log::debug(sprintf('Going to group %d found journal(s)', count($result))); - $grouped = $this->groupByCurrency($result); + $grouped = $this->groupByCurrency($result); $this->saveGroupedAsStatistics($account, $start, $end, $type, $grouped); return $grouped; } - $grouped = [ + $grouped = [ 'count' => 0, ]; /** @var PeriodStatistic $statistic */ foreach ($statistics as $statistic) { - $id = (int)$statistic->transaction_currency_id; - $currency = Amount::getTransactionCurrencyById($id); - $grouped[$id] = [ + $id = (int)$statistic->transaction_currency_id; + $currency = Amount::getTransactionCurrencyById($id); + $grouped[$id] = [ 'amount' => (string)$statistic->amount, 'count' => (int)$statistic->count, 'currency_id' => $currency->id, @@ -435,11 +439,11 @@ trait PeriodOverview */ protected function getTagPeriodOverview(Tag $tag, Carbon $start, Carbon $end): array // period overview for tags. { - $range = Navigation::getViewRange(true); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; // properties for cache - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('tag-period-entries'); @@ -449,37 +453,37 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); - $transferSet = $collector->getExtractedJournals(); + $transferSet = $collector->getExtractedJournals(); // filer all of them: - $earnedSet = $this->filterJournalsByTag($earnedSet, $tag); - $spentSet = $this->filterJournalsByTag($spentSet, $tag); - $transferSet = $this->filterJournalsByTag($transferSet, $tag); + $earnedSet = $this->filterJournalsByTag($earnedSet, $tag); + $spentSet = $this->filterJournalsByTag($spentSet, $tag); + $transferSet = $this->filterJournalsByTag($transferSet, $tag); foreach ($dates as $currentDate) { $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); @@ -488,17 +492,17 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'transactions' => 0, - 'title' => $title, - 'route' => route( - 'tags.show', - [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] - ), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'transactions' => 0, + 'title' => $title, + 'route' => route( + 'tags.show', + [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] + ), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } return $entries; @@ -509,12 +513,12 @@ trait PeriodOverview */ protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); - $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); + $range = Navigation::getViewRange(true); + $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; // properties for cache - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('transactions-period-entries'); @@ -524,16 +528,16 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - $spent = []; - $earned = []; - $transferred = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; + $spent = []; + $earned = []; + $transferred = []; // collect all journals in this period (regardless of type) - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTypes($types)->setRange($start, $end); - $genericSet = $collector->getExtractedJournals(); - $loops = 0; + $genericSet = $collector->getExtractedJournals(); + $loops = 0; foreach ($dates as $currentDate) { $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); @@ -551,14 +555,14 @@ trait PeriodOverview } } $entries[] - = [ - 'title' => $title, - 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + = [ + 'title' => $title, + 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; ++$loops; } @@ -599,7 +603,7 @@ trait PeriodOverview { $return = []; foreach ($set as $entry) { - $found = false; + $found = false; /** @var array $localTag */ foreach ($entry['tags'] as $localTag) { @@ -616,7 +620,7 @@ trait PeriodOverview return $return; } - private function filterTransactionsByType(TransactionTypeEnum $type, array $transactions, Carbon $start, Carbon $end): array + private function filterTransactionsByType(TransactionTypeEnum $type, Carbon $start, Carbon $end): array { $result = []; @@ -624,12 +628,11 @@ trait PeriodOverview * @var int $index * @var array $item */ - foreach ($transactions as $index => $item) { + foreach ($this->transactions as $item) { $date = Carbon::parse($item['date']); $fits = $item['type'] === $type->value && $date >= $start && $date <= $end; if ($fits) { $result[] = $item; - unset($transactions[$index]); } } @@ -670,7 +673,7 @@ trait PeriodOverview return $return; } - private function filterTransfers(string $direction, array $transactions, Carbon $start, Carbon $end): array + private function filterTransfers(string $direction, Carbon $start, Carbon $end): array { $result = []; @@ -678,7 +681,7 @@ trait PeriodOverview * @var int $index * @var array $item */ - foreach ($transactions as $index => $item) { + foreach ($this->transactions as $item) { $date = Carbon::parse($item['date']); if ($date >= $start && $date <= $end) { if ('Transfer' === $item['type'] && 'away' === $direction && -1 === bccomp((string)$item['amount'], '0')) { @@ -688,8 +691,6 @@ trait PeriodOverview } if ('Transfer' === $item['type'] && 'in' === $direction && 1 === bccomp((string)$item['amount'], '0')) { $result[] = $item; - - continue; } } } @@ -714,13 +715,13 @@ trait PeriodOverview exit; } - $currencyId = (int)$journal['currency_id']; - $currencyCode = $journal['currency_code']; - $currencyName = $journal['currency_name']; - $currencySymbol = $journal['currency_symbol']; - $currencyDecimalPlaces = $journal['currency_decimal_places']; - $foreignCurrencyId = $journal['foreign_currency_id']; - $amount = $journal['amount'] ?? '0'; + $currencyId = (int)$journal['currency_id']; + $currencyCode = $journal['currency_code']; + $currencyName = $journal['currency_name']; + $currencySymbol = $journal['currency_symbol']; + $currencyDecimalPlaces = $journal['currency_decimal_places']; + $foreignCurrencyId = $journal['foreign_currency_id']; + $amount = $journal['amount'] ?? '0'; if ($this->convertToPrimary && $currencyId !== $this->primaryCurrency->id && $foreignCurrencyId !== $this->primaryCurrency->id) { $amount = $journal['pc_amount'] ?? '0'; From d61f87f64994b52110d244fc55a39cdbce74b712 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 26 Sep 2025 20:47:44 +0200 Subject: [PATCH 49/91] Fix URL --- app/Support/Http/Controllers/PeriodOverview.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index 8e0d68f206..4081faba0c 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -330,7 +330,7 @@ trait PeriodOverview $types = ['spent', 'earned', 'transferred_in', 'transferred_away']; $return = [ 'title' => Navigation::periodShow($start, $period), - 'route' => route('accounts.show', [$account->id, $start->format('Y-m-d'), $start->format('Y-m-d')]), + 'route' => route('accounts.show', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]), 'total_transactions' => 0, ]; $this->transactions = []; From eb6f78406e71753ee4b2cee68fb8546e73853d6d Mon Sep 17 00:00:00 2001 From: JC5 Date: Fri, 26 Sep 2025 21:25:46 +0200 Subject: [PATCH 50/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-09-26?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/DebugController.php | 2 +- app/Models/PeriodStatistic.php | 5 +- .../Http/Controllers/PeriodOverview.php | 230 +++++++++--------- .../Report/Budget/BudgetReportGenerator.php | 20 +- config/firefly.php | 2 +- 5 files changed, 129 insertions(+), 130 deletions(-) diff --git a/app/Http/Controllers/DebugController.php b/app/Http/Controllers/DebugController.php index f599788536..377bacd735 100644 --- a/app/Http/Controllers/DebugController.php +++ b/app/Http/Controllers/DebugController.php @@ -109,7 +109,7 @@ class DebugController extends Controller Artisan::call('route:clear'); Artisan::call('view:clear'); - PeriodStatistic::where('id','>',0)->delete(); + PeriodStatistic::where('id', '>', 0)->delete(); // also do some recalculations. Artisan::call('correction:recalculates-liabilities'); diff --git a/app/Models/PeriodStatistic.php b/app/Models/PeriodStatistic.php index af29deb02a..9a167fafe0 100644 --- a/app/Models/PeriodStatistic.php +++ b/app/Models/PeriodStatistic.php @@ -9,7 +9,6 @@ use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\MorphTo; -use Illuminate\Database\Eloquent\SoftDeletes; class PeriodStatistic extends Model { @@ -20,8 +19,8 @@ class PeriodStatistic extends Model return [ 'created_at' => 'datetime', 'updated_at' => 'datetime', - 'start' => SeparateTimezoneCaster::class, - 'end' => SeparateTimezoneCaster::class, + 'start' => SeparateTimezoneCaster::class, + 'end' => SeparateTimezoneCaster::class, ]; } diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index 4081faba0c..b87dda9531 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -89,12 +89,12 @@ trait PeriodOverview $this->accountRepository = app(AccountRepositoryInterface::class); $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); $range = Navigation::getViewRange(true); - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); - $this->statistics = $this->periodStatisticRepo->allInRangeForModel($account, $start, $end); + $dates = Navigation::blockPeriods($start, $end, $range); + [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); + $this->statistics = $this->periodStatisticRepo->allInRangeForModel($account, $start, $end); // TODO needs to be re-arranged: // get all period stats for entire range. @@ -102,7 +102,7 @@ trait PeriodOverview // create new ones, or use collected. - $entries = []; + $entries = []; Log::debug(sprintf('Count of loops: %d', count($dates))); foreach ($dates as $currentDate) { $entries[] = $this->getSingleAccountPeriod($account, $currentDate['period'], $currentDate['start'], $currentDate['end']); @@ -138,11 +138,11 @@ trait PeriodOverview */ protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; // properties for entries with their amounts. - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty($range); @@ -154,32 +154,32 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setCategory($category); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setCategory($category); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setCategory($category); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); - $transferSet = $collector->getExtractedJournals(); + $transferSet = $collector->getExtractedJournals(); foreach ($dates as $currentDate) { $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); $earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']); @@ -187,17 +187,17 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'transactions' => 0, - 'title' => $title, - 'route' => route( - 'categories.show', - [$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] - ), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'transactions' => 0, + 'title' => $title, + 'route' => route( + 'categories.show', + [$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] + ), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } $cache->store($entries); @@ -213,11 +213,11 @@ trait PeriodOverview */ protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty($this->convertToPrimary); @@ -228,28 +228,28 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // get all expenses without a budget. /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end)->withoutBudget()->withAccountInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $journals = $collector->getExtractedJournals(); + $journals = $collector->getExtractedJournals(); foreach ($dates as $currentDate) { $set = $this->filterJournalsByDate($journals, $currentDate['start'], $currentDate['end']); $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'title' => $title, - 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($set), - 'spent' => $this->groupByCurrency($set), - 'earned' => [], - 'transferred_away' => [], - 'transferred_in' => [], - ]; + 'title' => $title, + 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($set), + 'spent' => $this->groupByCurrency($set), + 'earned' => [], + 'transferred_away' => [], + 'transferred_in' => [], + ]; } $cache->store($entries); @@ -266,38 +266,38 @@ trait PeriodOverview protected function getNoCategoryPeriodOverview(Carbon $theDate): array { Log::debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d'))); - $range = Navigation::getViewRange(true); - $first = $this->journalRepos->firstNull(); - $start = null === $first ? new Carbon() : $first->date; - $end = clone $theDate; - $end = Navigation::endOfPeriod($end, $range); + $range = Navigation::getViewRange(true); + $first = $this->journalRepos->firstNull(); + $start = null === $first ? new Carbon() : $first->date; + $end = clone $theDate; + $end = Navigation::endOfPeriod($end, $range); Log::debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d'))); Log::debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d'))); // properties for cache - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); @@ -311,13 +311,13 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'title' => $title, - 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'title' => $title, + 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } Log::debug('End of loops'); @@ -335,7 +335,7 @@ trait PeriodOverview ]; $this->transactions = []; foreach ($types as $type) { - $set = $this->getSingleAccountPeriodByType($account, $start, $end, $type); + $set = $this->getSingleAccountPeriodByType($account, $start, $end, $type); $return['total_transactions'] += $set['count']; unset($set['count']); $return[$type] = $set; @@ -409,15 +409,15 @@ trait PeriodOverview return $grouped; } - $grouped = [ + $grouped = [ 'count' => 0, ]; /** @var PeriodStatistic $statistic */ foreach ($statistics as $statistic) { - $id = (int)$statistic->transaction_currency_id; - $currency = Amount::getTransactionCurrencyById($id); - $grouped[$id] = [ + $id = (int)$statistic->transaction_currency_id; + $currency = Amount::getTransactionCurrencyById($id); + $grouped[$id] = [ 'amount' => (string)$statistic->amount, 'count' => (int)$statistic->count, 'currency_id' => $currency->id, @@ -439,11 +439,11 @@ trait PeriodOverview */ protected function getTagPeriodOverview(Tag $tag, Carbon $start, Carbon $end): array // period overview for tags. { - $range = Navigation::getViewRange(true); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; // properties for cache - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('tag-period-entries'); @@ -453,37 +453,37 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); - $transferSet = $collector->getExtractedJournals(); + $transferSet = $collector->getExtractedJournals(); // filer all of them: - $earnedSet = $this->filterJournalsByTag($earnedSet, $tag); - $spentSet = $this->filterJournalsByTag($spentSet, $tag); - $transferSet = $this->filterJournalsByTag($transferSet, $tag); + $earnedSet = $this->filterJournalsByTag($earnedSet, $tag); + $spentSet = $this->filterJournalsByTag($spentSet, $tag); + $transferSet = $this->filterJournalsByTag($transferSet, $tag); foreach ($dates as $currentDate) { $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); @@ -492,17 +492,17 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'transactions' => 0, - 'title' => $title, - 'route' => route( - 'tags.show', - [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] - ), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'transactions' => 0, + 'title' => $title, + 'route' => route( + 'tags.show', + [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] + ), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } return $entries; @@ -513,12 +513,12 @@ trait PeriodOverview */ protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); - $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); + $range = Navigation::getViewRange(true); + $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; // properties for cache - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('transactions-period-entries'); @@ -528,16 +528,16 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - $spent = []; - $earned = []; - $transferred = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; + $spent = []; + $earned = []; + $transferred = []; // collect all journals in this period (regardless of type) - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTypes($types)->setRange($start, $end); - $genericSet = $collector->getExtractedJournals(); - $loops = 0; + $genericSet = $collector->getExtractedJournals(); + $loops = 0; foreach ($dates as $currentDate) { $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); @@ -555,14 +555,14 @@ trait PeriodOverview } } $entries[] - = [ - 'title' => $title, - 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + = [ + 'title' => $title, + 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; ++$loops; } @@ -603,7 +603,7 @@ trait PeriodOverview { $return = []; foreach ($set as $entry) { - $found = false; + $found = false; /** @var array $localTag */ foreach ($entry['tags'] as $localTag) { @@ -715,13 +715,13 @@ trait PeriodOverview exit; } - $currencyId = (int)$journal['currency_id']; - $currencyCode = $journal['currency_code']; - $currencyName = $journal['currency_name']; - $currencySymbol = $journal['currency_symbol']; - $currencyDecimalPlaces = $journal['currency_decimal_places']; - $foreignCurrencyId = $journal['foreign_currency_id']; - $amount = $journal['amount'] ?? '0'; + $currencyId = (int)$journal['currency_id']; + $currencyCode = $journal['currency_code']; + $currencyName = $journal['currency_name']; + $currencySymbol = $journal['currency_symbol']; + $currencyDecimalPlaces = $journal['currency_decimal_places']; + $foreignCurrencyId = $journal['foreign_currency_id']; + $amount = $journal['amount'] ?? '0'; if ($this->convertToPrimary && $currencyId !== $this->primaryCurrency->id && $foreignCurrencyId !== $this->primaryCurrency->id) { $amount = $journal['pc_amount'] ?? '0'; diff --git a/app/Support/Report/Budget/BudgetReportGenerator.php b/app/Support/Report/Budget/BudgetReportGenerator.php index 2fd81217bb..4de1c9d9a6 100644 --- a/app/Support/Report/Budget/BudgetReportGenerator.php +++ b/app/Support/Report/Budget/BudgetReportGenerator.php @@ -334,16 +334,16 @@ class BudgetReportGenerator // make sum information: $this->report['sums'][$currencyId] ??= [ - 'budgeted' => '0', - 'spent' => '0', - 'left' => '0', - 'overspent' => '0', - 'currency_id' => $currencyId, - 'currency_code' => $limitCurrency->code, - 'currency_name' => $limitCurrency->name, - 'currency_symbol' => $limitCurrency->symbol, - 'currency_decimal_places' => $limitCurrency->decimal_places, - ]; + 'budgeted' => '0', + 'spent' => '0', + 'left' => '0', + 'overspent' => '0', + 'currency_id' => $currencyId, + 'currency_code' => $limitCurrency->code, + 'currency_name' => $limitCurrency->name, + 'currency_symbol' => $limitCurrency->symbol, + 'currency_decimal_places' => $limitCurrency->decimal_places, + ]; $this->report['sums'][$currencyId]['budgeted'] = bcadd((string)$this->report['sums'][$currencyId]['budgeted'], $limit->amount); $this->report['sums'][$currencyId]['spent'] = bcadd((string)$this->report['sums'][$currencyId]['spent'], $spent); $this->report['sums'][$currencyId]['left'] = bcadd((string)$this->report['sums'][$currencyId]['left'], bcadd($limit->amount, $spent)); diff --git a/config/firefly.php b/config/firefly.php index cf0424ddc6..c990ec4a8e 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -79,7 +79,7 @@ return [ // see cer.php for exchange rates feature flag. ], 'version' => 'develop/2025-09-26', - 'build_time' => 1758908498, + 'build_time' => 1758914637, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 27, From c54da6200599d26b842a263457c8ba74d7b7359e Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 27 Sep 2025 05:31:26 +0200 Subject: [PATCH 51/91] Enable period statistics for category. --- app/Models/Category.php | 5 + .../Category/CategoryRepository.php | 39 ++++++ .../Category/CategoryRepositoryInterface.php | 2 + .../Http/Controllers/PeriodOverview.php | 129 +++++++++++++++--- 4 files changed, 158 insertions(+), 17 deletions(-) diff --git a/app/Models/Category.php b/app/Models/Category.php index 018fbdbef4..55c9c7ebcf 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -109,4 +109,9 @@ class Category extends Model 'user_group_id' => 'integer', ]; } + + public function primaryPeriodStatistics(): MorphMany + { + return $this->morphMany(PeriodStatistic::class, 'primary_statable'); + } } diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 487a467c20..f644c57070 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -358,4 +358,43 @@ class CategoryRepository implements CategoryRepositoryInterface, UserGroupInterf return $service->update($category, $data); } + + public function periodCollection(Category $category, Carbon $start, Carbon $end): array + { + Log::debug(sprintf('periodCollection(#%d, %s, %s)', $category->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); + + return $category->transactionJournals() + ->leftJoin('transactions','transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') + ->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id') + ->leftJoin('transaction_currencies as foreign_currencies', 'foreign_currencies.id', '=', 'transactions.foreign_currency_id') + ->where('transaction_journals.date', '>=', $start) + ->where('transaction_journals.date', '<=', $end) + ->where('transactions.amount', '>', 0) + ->get([ + // currencies + 'transaction_currencies.id as currency_id', + 'transaction_currencies.code as currency_code', + 'transaction_currencies.name as currency_name', + 'transaction_currencies.symbol as currency_symbol', + 'transaction_currencies.decimal_places as currency_decimal_places', + + // foreign + 'foreign_currencies.id as foreign_currency_id', + 'foreign_currencies.code as foreign_currency_code', + 'foreign_currencies.name as foreign_currency_name', + 'foreign_currencies.symbol as foreign_currency_symbol', + 'foreign_currencies.decimal_places as foreign_currency_decimal_places', + + // fields + 'transaction_journals.date', + 'transaction_types.type', + 'transaction_journals.transaction_currency_id', + 'transactions.amount', + 'transactions.native_amount as pc_amount', + 'transactions.foreign_amount', + ]) + ->toArray() + ; + } } diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index 263c11c716..cef58d2d17 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -48,6 +48,8 @@ interface CategoryRepositoryInterface public function categoryStartsWith(string $query, int $limit): Collection; + public function periodCollection(Category $category, Carbon $start, Carbon $end): array; + public function destroy(Category $category): bool; /** diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index 4081faba0c..1995bc15c8 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -27,17 +27,20 @@ namespace FireflyIII\Support\Http\Controllers; use Carbon\Carbon; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Generator\Report\Category\YearReportGenerator; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Account; use FireflyIII\Models\Category; use FireflyIII\Models\PeriodStatistic; use FireflyIII\Models\Tag; use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface; use FireflyIII\Support\CacheProperties; use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\Navigation; +use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; @@ -71,6 +74,7 @@ use Illuminate\Support\Facades\Log; trait PeriodOverview { protected AccountRepositoryInterface $accountRepository; + protected CategoryRepositoryInterface $categoryRepository; protected JournalRepositoryInterface $journalRepos; protected PeriodStatisticRepositoryInterface $periodStatisticRepo; private Collection $statistics; // temp data holder @@ -87,6 +91,7 @@ trait PeriodOverview { Log::debug(sprintf('Now in getAccountPeriodOverview(#%d, %s %s)', $account->id, $start->format('Y-m-d H:i:s.u'), $end->format('Y-m-d H:i:s.u'))); $this->accountRepository = app(AccountRepositoryInterface::class); + $this->accountRepository->setUser($account->user); $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; @@ -138,24 +143,27 @@ trait PeriodOverview */ protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array { + $this->categoryRepository = app(CategoryRepositoryInterface::class); + $this->categoryRepository->setUser($category->user); + $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; - // properties for entries with their amounts. - $cache = new CacheProperties(); - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty($range); - $cache->addProperty('category-show-period-entries'); - $cache->addProperty($category->id); - - if ($cache->has()) { - return $cache->get(); - } - /** @var array $dates */ $dates = Navigation::blockPeriods($start, $end, $range); $entries = []; + [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); + $this->statistics = $this->periodStatisticRepo->allInRangeForModel($category, $start, $end); + + + Log::debug(sprintf('Count of loops: %d', count($dates))); + foreach ($dates as $currentDate) { + $entries[] = $this->getSingleCategoryPeriod($category, $currentDate['period'], $currentDate['start'], $currentDate['end']); + } + + return $entries; + // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ @@ -199,7 +207,6 @@ trait PeriodOverview 'transferred' => $this->groupByCurrency($transferred), ]; } - $cache->store($entries); return $entries; } @@ -324,6 +331,7 @@ trait PeriodOverview return $entries; } + protected function getSingleAccountPeriod(Account $account, string $period, Carbon $start, Carbon $end): array { Log::debug(sprintf('Now in getSingleAccountPeriod(#%d, %s %s)', $account->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); @@ -344,6 +352,26 @@ trait PeriodOverview return $return; } + protected function getSingleCategoryPeriod(Category $category, string $period, Carbon $start, Carbon $end): array + { + Log::debug(sprintf('Now in getSingleCategoryPeriod(#%d, %s %s)', $category->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); + $types = ['spent', 'earned', 'transferred_in', 'transferred_away']; + $return = [ + 'title' => Navigation::periodShow($start, $period), + 'route' => route('categories.show', [$category->id, $start->format('Y-m-d'), $end->format('Y-m-d')]), + 'total_transactions' => 0, + ]; + $this->transactions = []; + foreach ($types as $type) { + $set = $this->getSingleCategoryPeriodByType($category, $start, $end, $type); + $return['total_transactions'] += $set['count']; + unset($set['count']); + $return[$type] = $set; + } + + return $return; + } + protected function filterStatistics(Carbon $start, Carbon $end, string $type): Collection { return $this->statistics->filter( @@ -432,6 +460,73 @@ trait PeriodOverview return $grouped; } + protected function getSingleCategoryPeriodByType(Category $category, Carbon $start, Carbon $end, string $type): array + { + Log::debug(sprintf('Now in getSingleCategoryPeriodByType(#%d, %s %s, %s)', $category->id, $start->format('Y-m-d'), $end->format('Y-m-d'), $type)); + $statistics = $this->filterStatistics($start, $end, $type); + + // nothing found, regenerate them. + if (0 === $statistics->count()) { + Log::debug(sprintf('Found nothing in this period for type "%s"', $type)); + if (0 === count($this->transactions)) { + $this->transactions = $this->categoryRepository->periodCollection($category, $start, $end); + } + + switch ($type) { + default: + throw new FireflyException(sprintf('Cannot deal with category period type %s', $type)); + + case 'spent': + $result = $this->filterTransactionsByType(TransactionTypeEnum::WITHDRAWAL, $start, $end); + + break; + + case 'earned': + $result = $this->filterTransactionsByType(TransactionTypeEnum::DEPOSIT, $start, $end); + + break; + + case 'transferred_in': + $result = $this->filterTransfers('in', $start, $end); + + break; + + case 'transferred_away': + $result = $this->filterTransfers('away', $start, $end); + + break; + } + // each result must be grouped by currency, then saved as period statistic. + Log::debug(sprintf('Going to group %d found journal(s)', count($result))); + $grouped = $this->groupByCurrency($result); + + $this->saveGroupedAsStatistics($category, $start, $end, $type, $grouped); + + return $grouped; + } + $grouped = [ + 'count' => 0, + ]; + + /** @var PeriodStatistic $statistic */ + foreach ($statistics as $statistic) { + $id = (int)$statistic->transaction_currency_id; + $currency = Amount::getTransactionCurrencyById($id); + $grouped[$id] = [ + 'amount' => (string)$statistic->amount, + 'count' => (int)$statistic->count, + 'currency_id' => $currency->id, + 'currency_name' => $currency->name, + 'currency_code' => $currency->code, + 'currency_symbol' => $currency->symbol, + 'currency_decimal_places' => $currency->decimal_places, + ]; + $grouped['count'] += (int)$statistic->count; + } + + return $grouped; + } + /** * This shows a period overview for a tag. It goes back in time and lists all relevant transactions and sums. * @@ -569,16 +664,16 @@ trait PeriodOverview return $entries; } - protected function saveGroupedAsStatistics(Account $account, Carbon $start, Carbon $end, string $type, array $array): void + protected function saveGroupedAsStatistics(Model $model, Carbon $start, Carbon $end, string $type, array $array): void { unset($array['count']); - Log::debug(sprintf('saveGroupedAsStatistics(#%d, %s, %s, "%s", array(%d))', $account->id, $start->format('Y-m-d'), $end->format('Y-m-d'), $type, count($array))); + Log::debug(sprintf('saveGroupedAsStatistics(%s #%d, %s, %s, "%s", array(%d))',get_class($model), $model->id, $start->format('Y-m-d'), $end->format('Y-m-d'), $type, count($array))); foreach ($array as $entry) { - $this->periodStatisticRepo->saveStatistic($account, $entry['currency_id'], $start, $end, $type, $entry['count'], $entry['amount']); + $this->periodStatisticRepo->saveStatistic($model, $entry['currency_id'], $start, $end, $type, $entry['count'], $entry['amount']); } if (0 === count($array)) { Log::debug('Save empty statistic.'); - $this->periodStatisticRepo->saveStatistic($account, $this->primaryCurrency->id, $start, $end, $type, 0, '0'); + $this->periodStatisticRepo->saveStatistic($model, $this->primaryCurrency->id, $start, $end, $type, 0, '0'); } } From 79f2d7021122de653f4c401b4e87e6601c3c6b3d Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 27 Sep 2025 05:35:20 +0200 Subject: [PATCH 52/91] Fix #10974 --- app/Handlers/Events/WebhookEventHandler.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/Handlers/Events/WebhookEventHandler.php b/app/Handlers/Events/WebhookEventHandler.php index adad80b942..bab7d51363 100644 --- a/app/Handlers/Events/WebhookEventHandler.php +++ b/app/Handlers/Events/WebhookEventHandler.php @@ -62,5 +62,8 @@ class WebhookEventHandler Log::debug(sprintf('Skip message #%d', $message->id)); } } + + // clean up sent messages table: + WebhookMessage::where('webhook_messages.sent', true)->where('webhook_messages.created_at', '<', now()->subDays(30))->delete(); } } From 0f0a28c3d9d03221509da878aaa7678f1a4ab412 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 27 Sep 2025 05:49:22 +0200 Subject: [PATCH 53/91] Add cached periods for tags. --- app/Models/Tag.php | 5 + app/Repositories/Tag/TagRepository.php | 40 ++ .../Tag/TagRepositoryInterface.php | 1 + .../Http/Controllers/PeriodOverview.php | 373 ++++++++++-------- 4 files changed, 260 insertions(+), 159 deletions(-) diff --git a/app/Models/Tag.php b/app/Models/Tag.php index c9fc2f134b..f348bea48a 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -104,4 +104,9 @@ class Tag extends Model 'user_group_id' => 'integer', ]; } + + public function primaryPeriodStatistics(): MorphMany + { + return $this->morphMany(PeriodStatistic::class, 'primary_statable'); + } } diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index 58c998e760..06a41da184 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -379,4 +379,44 @@ class TagRepository implements TagRepositoryInterface, UserGroupInterface /** @var null|Location */ return $tag->locations()->first(); } + + #[\Override] + public function periodCollection(Tag $tag, Carbon $start, Carbon $end): array + { + Log::debug(sprintf('periodCollection(#%d, %s, %s)', $tag->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); + + return $tag->transactionJournals() + ->leftJoin('transactions','transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') + ->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id') + ->leftJoin('transaction_currencies as foreign_currencies', 'foreign_currencies.id', '=', 'transactions.foreign_currency_id') + ->where('transaction_journals.date', '>=', $start) + ->where('transaction_journals.date', '<=', $end) + ->where('transactions.amount', '>', 0) + ->get([ + // currencies + 'transaction_currencies.id as currency_id', + 'transaction_currencies.code as currency_code', + 'transaction_currencies.name as currency_name', + 'transaction_currencies.symbol as currency_symbol', + 'transaction_currencies.decimal_places as currency_decimal_places', + + // foreign + 'foreign_currencies.id as foreign_currency_id', + 'foreign_currencies.code as foreign_currency_code', + 'foreign_currencies.name as foreign_currency_name', + 'foreign_currencies.symbol as foreign_currency_symbol', + 'foreign_currencies.decimal_places as foreign_currency_decimal_places', + + // fields + 'transaction_journals.date', + 'transaction_types.type', + 'transaction_journals.transaction_currency_id', + 'transactions.amount', + 'transactions.native_amount as pc_amount', + 'transactions.foreign_amount', + ]) + ->toArray() + ; + } } diff --git a/app/Repositories/Tag/TagRepositoryInterface.php b/app/Repositories/Tag/TagRepositoryInterface.php index 2052dff88c..18e45fb9ce 100644 --- a/app/Repositories/Tag/TagRepositoryInterface.php +++ b/app/Repositories/Tag/TagRepositoryInterface.php @@ -50,6 +50,7 @@ interface TagRepositoryInterface * This method destroys a tag. */ public function destroy(Tag $tag): bool; + public function periodCollection(Tag $tag, Carbon $start, Carbon $end): array; /** * Destroy all tags. diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index 9cdf54d6e3..3b9a708d88 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -27,7 +27,6 @@ namespace FireflyIII\Support\Http\Controllers; use Carbon\Carbon; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Generator\Report\Category\YearReportGenerator; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Account; use FireflyIII\Models\Category; @@ -37,9 +36,11 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Support\CacheProperties; use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\Navigation; +use FireflyIII\Support\Facades\Steam; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; @@ -74,7 +75,8 @@ use Illuminate\Support\Facades\Log; trait PeriodOverview { protected AccountRepositoryInterface $accountRepository; - protected CategoryRepositoryInterface $categoryRepository; + protected CategoryRepositoryInterface $categoryRepository; + protected TagRepositoryInterface $tagRepository; protected JournalRepositoryInterface $journalRepos; protected PeriodStatisticRepositoryInterface $periodStatisticRepo; private Collection $statistics; // temp data holder @@ -90,16 +92,16 @@ trait PeriodOverview protected function getAccountPeriodOverview(Account $account, Carbon $start, Carbon $end): array { Log::debug(sprintf('Now in getAccountPeriodOverview(#%d, %s %s)', $account->id, $start->format('Y-m-d H:i:s.u'), $end->format('Y-m-d H:i:s.u'))); - $this->accountRepository = app(AccountRepositoryInterface::class); + $this->accountRepository = app(AccountRepositoryInterface::class); $this->accountRepository->setUser($account->user); $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); $range = Navigation::getViewRange(true); - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); - $this->statistics = $this->periodStatisticRepo->allInRangeForModel($account, $start, $end); + $dates = Navigation::blockPeriods($start, $end, $range); + [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); + $this->statistics = $this->periodStatisticRepo->allInRangeForModel($account, $start, $end); // TODO needs to be re-arranged: // get all period stats for entire range. @@ -107,7 +109,7 @@ trait PeriodOverview // create new ones, or use collected. - $entries = []; + $entries = []; Log::debug(sprintf('Count of loops: %d', count($dates))); foreach ($dates as $currentDate) { $entries[] = $this->getSingleAccountPeriod($account, $currentDate['period'], $currentDate['start'], $currentDate['end']); @@ -143,7 +145,7 @@ trait PeriodOverview */ protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array { - $this->categoryRepository = app(CategoryRepositoryInterface::class); + $this->categoryRepository = app(CategoryRepositoryInterface::class); $this->categoryRepository->setUser($category->user); $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); @@ -163,51 +165,6 @@ trait PeriodOverview } return $entries; - - // collect all expenses in this period: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->setCategory($category); - $collector->setRange($start, $end); - $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); - - // collect all income in this period: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->setCategory($category); - $collector->setRange($start, $end); - $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); - - // collect all transfers in this period: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->setCategory($category); - $collector->setRange($start, $end); - $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); - $transferSet = $collector->getExtractedJournals(); - foreach ($dates as $currentDate) { - $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); - $earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']); - $transferred = $this->filterJournalsByDate($transferSet, $currentDate['start'], $currentDate['end']); - $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); - $entries[] - = [ - 'transactions' => 0, - 'title' => $title, - 'route' => route( - 'categories.show', - [$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] - ), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; - } - - return $entries; } /** @@ -219,11 +176,11 @@ trait PeriodOverview */ protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty($this->convertToPrimary); @@ -234,28 +191,28 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // get all expenses without a budget. /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end)->withoutBudget()->withAccountInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $journals = $collector->getExtractedJournals(); + $journals = $collector->getExtractedJournals(); foreach ($dates as $currentDate) { $set = $this->filterJournalsByDate($journals, $currentDate['start'], $currentDate['end']); $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'title' => $title, - 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($set), - 'spent' => $this->groupByCurrency($set), - 'earned' => [], - 'transferred_away' => [], - 'transferred_in' => [], - ]; + 'title' => $title, + 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($set), + 'spent' => $this->groupByCurrency($set), + 'earned' => [], + 'transferred_away' => [], + 'transferred_in' => [], + ]; } $cache->store($entries); @@ -272,38 +229,38 @@ trait PeriodOverview protected function getNoCategoryPeriodOverview(Carbon $theDate): array { Log::debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d'))); - $range = Navigation::getViewRange(true); - $first = $this->journalRepos->firstNull(); - $start = null === $first ? new Carbon() : $first->date; - $end = clone $theDate; - $end = Navigation::endOfPeriod($end, $range); + $range = Navigation::getViewRange(true); + $first = $this->journalRepos->firstNull(); + $start = null === $first ? new Carbon() : $first->date; + $end = clone $theDate; + $end = Navigation::endOfPeriod($end, $range); Log::debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d'))); Log::debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d'))); // properties for cache - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); @@ -317,13 +274,13 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'title' => $title, - 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'title' => $title, + 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } Log::debug('End of loops'); @@ -342,7 +299,7 @@ trait PeriodOverview ]; $this->transactions = []; foreach ($types as $type) { - $set = $this->getSingleAccountPeriodByType($account, $start, $end, $type); + $set = $this->getSingleAccountPeriodByType($account, $start, $end, $type); $return['total_transactions'] += $set['count']; unset($set['count']); $return[$type] = $set; @@ -371,6 +328,26 @@ trait PeriodOverview return $return; } + protected function getSingleTagPeriod(Tag $tag, string $period, Carbon $start, Carbon $end): array + { + Log::debug(sprintf('Now in getSingleTagPeriod(#%d, %s %s)', $tag->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); + $types = ['spent', 'earned', 'transferred_in', 'transferred_away']; + $return = [ + 'title' => Navigation::periodShow($start, $period), + 'route' => route('tags.show', [$tag->id, $start->format('Y-m-d'), $end->format('Y-m-d')]), + 'total_transactions' => 0, + ]; + $this->transactions = []; + foreach ($types as $type) { + $set = $this->getSingleTagPeriodByType($tag, $start, $end, $type); + $return['total_transactions'] += $set['count']; + unset($set['count']); + $return[$type] = $set; + } + + return $return; + } + protected function filterStatistics(Carbon $start, Carbon $end, string $type): Collection { return $this->statistics->filter( @@ -436,15 +413,15 @@ trait PeriodOverview return $grouped; } - $grouped = [ + $grouped = [ 'count' => 0, ]; /** @var PeriodStatistic $statistic */ foreach ($statistics as $statistic) { - $id = (int)$statistic->transaction_currency_id; - $currency = Amount::getTransactionCurrencyById($id); - $grouped[$id] = [ + $id = (int)$statistic->transaction_currency_id; + $currency = Amount::getTransactionCurrencyById($id); + $grouped[$id] = [ 'amount' => (string)$statistic->amount, 'count' => (int)$statistic->count, 'currency_id' => $currency->id, @@ -476,6 +453,7 @@ trait PeriodOverview throw new FireflyException(sprintf('Cannot deal with category period type %s', $type)); case 'spent': + $result = $this->filterTransactionsByType(TransactionTypeEnum::WITHDRAWAL, $start, $end); break; @@ -526,6 +504,74 @@ trait PeriodOverview return $grouped; } + protected function getSingleTagPeriodByType(Tag $tag, Carbon $start, Carbon $end, string $type): array + { + Log::debug(sprintf('Now in getSingleTagPeriodByType(#%d, %s %s, %s)', $tag->id, $start->format('Y-m-d'), $end->format('Y-m-d'), $type)); + $statistics = $this->filterStatistics($start, $end, $type); + + // nothing found, regenerate them. + if (0 === $statistics->count()) { + Log::debug(sprintf('Found nothing in this period for type "%s"', $type)); + if (0 === count($this->transactions)) { + $this->transactions = $this->tagRepository->periodCollection($tag, $start, $end); + } + + switch ($type) { + default: + throw new FireflyException(sprintf('Cannot deal with tag period type %s', $type)); + + case 'spent': + + $result = $this->filterTransactionsByType(TransactionTypeEnum::WITHDRAWAL, $start, $end); + + break; + + case 'earned': + $result = $this->filterTransactionsByType(TransactionTypeEnum::DEPOSIT, $start, $end); + + break; + + case 'transferred_in': + $result = $this->filterTransfers('in', $start, $end); + + break; + + case 'transferred_away': + $result = $this->filterTransfers('away', $start, $end); + + break; + } + // each result must be grouped by currency, then saved as period statistic. + Log::debug(sprintf('Going to group %d found journal(s)', count($result))); + $grouped = $this->groupByCurrency($result); + + $this->saveGroupedAsStatistics($tag, $start, $end, $type, $grouped); + + return $grouped; + } + $grouped = [ + 'count' => 0, + ]; + + /** @var PeriodStatistic $statistic */ + foreach ($statistics as $statistic) { + $id = (int)$statistic->transaction_currency_id; + $currency = Amount::getTransactionCurrencyById($id); + $grouped[$id] = [ + 'amount' => (string)$statistic->amount, + 'count' => (int)$statistic->count, + 'currency_id' => $currency->id, + 'currency_name' => $currency->name, + 'currency_code' => $currency->code, + 'currency_symbol' => $currency->symbol, + 'currency_decimal_places' => $currency->decimal_places, + ]; + $grouped['count'] += (int)$statistic->count; + } + + return $grouped; + } + /** * This shows a period overview for a tag. It goes back in time and lists all relevant transactions and sums. * @@ -533,51 +579,63 @@ trait PeriodOverview */ protected function getTagPeriodOverview(Tag $tag, Carbon $start, Carbon $end): array // period overview for tags. { - $range = Navigation::getViewRange(true); + $this->tagRepository = app(TagRepositoryInterface::class); + $this->tagRepository->setUser($tag->user); + $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); + + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; - // properties for cache - $cache = new CacheProperties(); - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty('tag-period-entries'); - $cache->addProperty($tag->id); - if ($cache->has()) { - return $cache->get(); + /** @var array $dates */ + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; + [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); + $this->statistics = $this->periodStatisticRepo->allInRangeForModel($tag, $start, $end); + + + Log::debug(sprintf('Count of loops: %d', count($dates))); + foreach ($dates as $currentDate) { + $entries[] = $this->getSingleTagPeriod($tag, $currentDate['period'], $currentDate['start'], $currentDate['end']); } + return $entries; + + + $range = Navigation::getViewRange(true); + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); - $transferSet = $collector->getExtractedJournals(); + $transferSet = $collector->getExtractedJournals(); // filer all of them: - $earnedSet = $this->filterJournalsByTag($earnedSet, $tag); - $spentSet = $this->filterJournalsByTag($spentSet, $tag); - $transferSet = $this->filterJournalsByTag($transferSet, $tag); + $earnedSet = $this->filterJournalsByTag($earnedSet, $tag); + $spentSet = $this->filterJournalsByTag($spentSet, $tag); + $transferSet = $this->filterJournalsByTag($transferSet, $tag); foreach ($dates as $currentDate) { $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); @@ -586,17 +644,17 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'transactions' => 0, - 'title' => $title, - 'route' => route( - 'tags.show', - [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] - ), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'transactions' => 0, + 'title' => $title, + 'route' => route( + 'tags.show', + [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] + ), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } return $entries; @@ -607,12 +665,12 @@ trait PeriodOverview */ protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); - $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); + $range = Navigation::getViewRange(true); + $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; // properties for cache - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('transactions-period-entries'); @@ -622,16 +680,16 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - $spent = []; - $earned = []; - $transferred = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; + $spent = []; + $earned = []; + $transferred = []; // collect all journals in this period (regardless of type) - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTypes($types)->setRange($start, $end); - $genericSet = $collector->getExtractedJournals(); - $loops = 0; + $genericSet = $collector->getExtractedJournals(); + $loops = 0; foreach ($dates as $currentDate) { $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); @@ -649,14 +707,14 @@ trait PeriodOverview } } $entries[] - = [ - 'title' => $title, - 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + = [ + 'title' => $title, + 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; ++$loops; } @@ -666,7 +724,7 @@ trait PeriodOverview protected function saveGroupedAsStatistics(Model $model, Carbon $start, Carbon $end, string $type, array $array): void { unset($array['count']); - Log::debug(sprintf('saveGroupedAsStatistics(%s #%d, %s, %s, "%s", array(%d))',get_class($model), $model->id, $start->format('Y-m-d'), $end->format('Y-m-d'), $type, count($array))); + Log::debug(sprintf('saveGroupedAsStatistics(%s #%d, %s, %s, "%s", array(%d))', get_class($model), $model->id, $start->format('Y-m-d'), $end->format('Y-m-d'), $type, count($array))); foreach ($array as $entry) { $this->periodStatisticRepo->saveStatistic($model, $entry['currency_id'], $start, $end, $type, $entry['count'], $entry['amount']); } @@ -697,7 +755,7 @@ trait PeriodOverview { $return = []; foreach ($set as $entry) { - $found = false; + $found = false; /** @var array $localTag */ foreach ($entry['tags'] as $localTag) { @@ -726,6 +784,11 @@ trait PeriodOverview $date = Carbon::parse($item['date']); $fits = $item['type'] === $type->value && $date >= $start && $date <= $end; if ($fits) { + + // if type is withdrawal, negative amount: + if (TransactionTypeEnum::WITHDRAWAL === $type && 1 === bccomp((string)$item['amount'], '0')) { + $item['amount'] = Steam::negative($item['amount']); + } $result[] = $item; } } @@ -801,21 +864,13 @@ trait PeriodOverview /** @var array $journal */ foreach ($journals as $journal) { - - if (!array_key_exists('currency_id', $journal)) { - Log::debug('very strange!'); - var_dump($journals); - - exit; - } - - $currencyId = (int)$journal['currency_id']; - $currencyCode = $journal['currency_code']; - $currencyName = $journal['currency_name']; - $currencySymbol = $journal['currency_symbol']; - $currencyDecimalPlaces = $journal['currency_decimal_places']; - $foreignCurrencyId = $journal['foreign_currency_id']; - $amount = $journal['amount'] ?? '0'; + $currencyId = (int)$journal['currency_id']; + $currencyCode = $journal['currency_code']; + $currencyName = $journal['currency_name']; + $currencySymbol = $journal['currency_symbol']; + $currencyDecimalPlaces = $journal['currency_decimal_places']; + $foreignCurrencyId = $journal['foreign_currency_id']; + $amount = $journal['amount'] ?? '0'; if ($this->convertToPrimary && $currencyId !== $this->primaryCurrency->id && $foreignCurrencyId !== $this->primaryCurrency->id) { $amount = $journal['pc_amount'] ?? '0'; From ac0113e445c3b9754d41fae333055613742e6ac6 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 27 Sep 2025 05:57:58 +0200 Subject: [PATCH 54/91] Fix some rector code. --- .../Autocomplete/PiggyBankController.php | 7 ++++-- .../DestroyController.php | 2 +- .../CurrencyExchangeRate/ShowController.php | 2 +- .../CurrencyExchangeRate/UpdateController.php | 2 +- app/Casts/SeparateTimezoneCaster.php | 1 - app/Console/Commands/Tools/ApplyRules.php | 3 ++- .../Transaction/CreateController.php | 8 ++++--- .../Category/OperationsRepository.php | 4 +--- .../PeriodStatisticRepository.php | 2 +- .../PiggyBank/ModifiesPiggyBanks.php | 16 +++++++------- app/Repositories/Tag/TagRepository.php | 3 ++- .../Support/CreditRecalculateService.php | 22 +++++++++---------- app/Support/Debug/Timer.php | 2 +- .../Http/Controllers/PeriodOverview.php | 4 ++-- .../Enrichments/CategoryEnrichment.php | 2 +- .../Enrichments/PiggyBankEnrichment.php | 10 ++++----- .../RecalculatesAvailableBudgetsTrait.php | 2 +- .../Summarizer/TransactionSummarizer.php | 4 ++-- .../Singleton/PreferencesSingleton.php | 2 +- app/Support/Steam.php | 2 +- .../PiggyBankEventTransformer.php | 2 +- 21 files changed, 53 insertions(+), 49 deletions(-) diff --git a/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php b/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php index de08a181b5..25fc1f145e 100644 --- a/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php +++ b/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php @@ -28,8 +28,10 @@ use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest; use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\PiggyBank; +use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; +use FireflyIII\Support\Facades\Amount; use Illuminate\Http\JsonResponse; /** @@ -96,6 +98,7 @@ class PiggyBankController extends Controller /** @var PiggyBank $piggy */ foreach ($piggies as $piggy) { + /** @var TransactionCurrency $currency */ $currency = $piggy->transactionCurrency; $currentAmount = $this->piggyRepository->getCurrentAmount($piggy); $objectGroup = $piggy->objectGroups()->first(); @@ -105,8 +108,8 @@ class PiggyBankController extends Controller 'name_with_balance' => sprintf( '%s (%s / %s)', $piggy->name, - app('amount')->formatAnything($currency, $currentAmount, false), - app('amount')->formatAnything($currency, $piggy->target_amount, false), + Amount::formatAnything($currency, $currentAmount, false), + Amount::formatAnything($currency, $piggy->target_amount, false), ), 'currency_id' => (string) $currency->id, 'currency_name' => $currency->name, diff --git a/app/Api/V1/Controllers/Models/CurrencyExchangeRate/DestroyController.php b/app/Api/V1/Controllers/Models/CurrencyExchangeRate/DestroyController.php index 265a72b616..733517a506 100644 --- a/app/Api/V1/Controllers/Models/CurrencyExchangeRate/DestroyController.php +++ b/app/Api/V1/Controllers/Models/CurrencyExchangeRate/DestroyController.php @@ -72,7 +72,7 @@ class DestroyController extends Controller public function destroySingleByDate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): JsonResponse { $exchangeRate = $this->repository->getSpecificRateOnDate($from, $to, $date); - if (null !== $exchangeRate) { + if ($exchangeRate instanceof CurrencyExchangeRate) { $this->repository->deleteRate($exchangeRate); } diff --git a/app/Api/V1/Controllers/Models/CurrencyExchangeRate/ShowController.php b/app/Api/V1/Controllers/Models/CurrencyExchangeRate/ShowController.php index 3b2f8c6e4d..22a9756ab7 100644 --- a/app/Api/V1/Controllers/Models/CurrencyExchangeRate/ShowController.php +++ b/app/Api/V1/Controllers/Models/CurrencyExchangeRate/ShowController.php @@ -95,7 +95,7 @@ class ShowController extends Controller $transformer->setParameters($this->parameters); $exchangeRate = $this->repository->getSpecificRateOnDate($from, $to, $date); - if (null === $exchangeRate) { + if (!$exchangeRate instanceof CurrencyExchangeRate) { throw new NotFoundHttpException(); } diff --git a/app/Api/V1/Controllers/Models/CurrencyExchangeRate/UpdateController.php b/app/Api/V1/Controllers/Models/CurrencyExchangeRate/UpdateController.php index 8844407f7d..8326f44c52 100644 --- a/app/Api/V1/Controllers/Models/CurrencyExchangeRate/UpdateController.php +++ b/app/Api/V1/Controllers/Models/CurrencyExchangeRate/UpdateController.php @@ -74,7 +74,7 @@ class UpdateController extends Controller public function updateByDate(UpdateRequest $request, TransactionCurrency $from, TransactionCurrency $to, Carbon $date): JsonResponse { $exchangeRate = $this->repository->getSpecificRateOnDate($from, $to, $date); - if (null === $exchangeRate) { + if (!$exchangeRate instanceof CurrencyExchangeRate) { throw new NotFoundHttpException(); } $date = $request->getDate(); diff --git a/app/Casts/SeparateTimezoneCaster.php b/app/Casts/SeparateTimezoneCaster.php index 67a56ae83e..608f5fc1c9 100644 --- a/app/Casts/SeparateTimezoneCaster.php +++ b/app/Casts/SeparateTimezoneCaster.php @@ -28,7 +28,6 @@ namespace FireflyIII\Casts; use Carbon\Carbon; use Illuminate\Contracts\Database\Eloquent\CastsAttributes; use Illuminate\Database\Eloquent\Model; -use Illuminate\Support\Facades\Log; /** * Class SeparateTimezoneCaster diff --git a/app/Console/Commands/Tools/ApplyRules.php b/app/Console/Commands/Tools/ApplyRules.php index b2a891b5d6..c0ea4ed2db 100644 --- a/app/Console/Commands/Tools/ApplyRules.php +++ b/app/Console/Commands/Tools/ApplyRules.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Tools; +use Carbon\CarbonInterface; use Carbon\Carbon; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Console\Commands\VerifiesAccessToken; @@ -283,7 +284,7 @@ class ApplyRules extends Command if (null !== $endString && '' !== $endString) { $inputEnd = Carbon::createFromFormat('Y-m-d', $endString); } - if (null === $inputEnd || null === $inputStart) { + if (!$inputEnd instanceof Carbon || null === $inputStart) { Log::error('Could not parse start or end date in verifyInputDate().'); return; diff --git a/app/Http/Controllers/Transaction/CreateController.php b/app/Http/Controllers/Transaction/CreateController.php index 519ff3d538..21c573565e 100644 --- a/app/Http/Controllers/Transaction/CreateController.php +++ b/app/Http/Controllers/Transaction/CreateController.php @@ -31,6 +31,7 @@ use FireflyIII\Http\Controllers\Controller; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface; use FireflyIII\Services\Internal\Update\GroupCloneService; +use FireflyIII\Support\Facades\Preferences; use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\View; use Illuminate\Http\JsonResponse; @@ -76,7 +77,7 @@ class CreateController extends Controller // event! event(new StoredTransactionGroup($newGroup, true, true)); - app('preferences')->mark(); + Preferences::mark(); $title = $newGroup->title ?? $newGroup->transactionJournals->first()->description; $link = route('transactions.show', [$newGroup->id]); @@ -103,7 +104,7 @@ class CreateController extends Controller * */ public function create(?string $objectType) { - app('preferences')->mark(); + Preferences::mark(); $sourceId = (int) request()->get('source'); $destinationId = (int) request()->get('destination'); @@ -114,7 +115,8 @@ class CreateController extends Controller $preFilled = session()->has('preFilled') ? session('preFilled') : []; $subTitle = (string) trans(sprintf('breadcrumbs.create_%s', strtolower((string) $objectType))); $subTitleIcon = 'fa-plus'; - $optionalFields = app('preferences')->get('transaction_journal_optional_fields', [])->data; + /** @var array|null $optionalFields */ + $optionalFields = Preferences::get('transaction_journal_optional_fields', [])->data; $allowedOpposingTypes = config('firefly.allowed_opposing_types'); $accountToTypes = config('firefly.account_to_transaction'); $previousUrl = $this->rememberPreviousUrl('transactions.create.url'); diff --git a/app/Repositories/Category/OperationsRepository.php b/app/Repositories/Category/OperationsRepository.php index e020782f40..d88bc16d0b 100644 --- a/app/Repositories/Category/OperationsRepository.php +++ b/app/Repositories/Category/OperationsRepository.php @@ -510,9 +510,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn $summarizer->setConvertToPrimary($convertToPrimary); // filter $journals by range AND currency if it is present. - $expenses = array_filter($expenses, static function (array $expense) use ($category): bool { - return $expense['category_id'] === $category->id; - }); + $expenses = array_filter($expenses, static fn(array $expense): bool => $expense['category_id'] === $category->id); return $summarizer->groupByCurrencyId($expenses, $method, false); } diff --git a/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php index 612773a9e2..07523c5348 100644 --- a/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php +++ b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php @@ -68,7 +68,7 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface Log::debug(sprintf( 'Saved #%d [currency #%d, Model %s #%d, %s to %s, %d, %s] as new statistic.', $stat->id, - get_class($model), + $model::class, $model->id, $stat->transaction_currency_id, $stat->start->toW3cString(), diff --git a/app/Repositories/PiggyBank/ModifiesPiggyBanks.php b/app/Repositories/PiggyBank/ModifiesPiggyBanks.php index 0acb4b7ed8..e63da28927 100644 --- a/app/Repositories/PiggyBank/ModifiesPiggyBanks.php +++ b/app/Repositories/PiggyBank/ModifiesPiggyBanks.php @@ -67,7 +67,7 @@ trait ModifiesPiggyBanks { $currentAmount = $this->getCurrentAmount($piggyBank, $account); $pivot = $piggyBank->accounts()->where('accounts.id', $account->id)->first()->pivot; - $pivot->current_amount = bcsub($currentAmount, $amount); + $pivot->current_amount = bcsub((string) $currentAmount, $amount); $pivot->native_current_amount = null; // also update native_current_amount. @@ -90,7 +90,7 @@ trait ModifiesPiggyBanks { $currentAmount = $this->getCurrentAmount($piggyBank, $account); $pivot = $piggyBank->accounts()->where('accounts.id', $account->id)->first()->pivot; - $pivot->current_amount = bcadd($currentAmount, $amount); + $pivot->current_amount = bcadd((string) $currentAmount, $amount); $pivot->native_current_amount = null; // also update native_current_amount. @@ -122,13 +122,13 @@ trait ModifiesPiggyBanks if (0 !== bccomp($piggyBank->target_amount, '0')) { - $leftToSave = bcsub($piggyBank->target_amount, $savedSoFar); - $maxAmount = 1 === bccomp($leftOnAccount, $leftToSave) ? $leftToSave : $leftOnAccount; + $leftToSave = bcsub($piggyBank->target_amount, (string) $savedSoFar); + $maxAmount = 1 === bccomp((string) $leftOnAccount, $leftToSave) ? $leftToSave : $leftOnAccount; Log::debug(sprintf('Left to save: %s', $leftToSave)); Log::debug(sprintf('Maximum amount: %s', $maxAmount)); } - $compare = bccomp($amount, $maxAmount); + $compare = bccomp($amount, (string) $maxAmount); $result = $compare <= 0; Log::debug(sprintf('Compare <= 0? %d, so canAddAmount is %s', $compare, var_export($result, true))); @@ -140,7 +140,7 @@ trait ModifiesPiggyBanks { $savedSoFar = $this->getCurrentAmount($piggyBank, $account); - return bccomp($amount, $savedSoFar) <= 0; + return bccomp($amount, (string) $savedSoFar) <= 0; } /** @@ -234,9 +234,9 @@ trait ModifiesPiggyBanks // if the piggy bank is now smaller than the sum of the money saved, // remove money from all accounts until the piggy bank is the right amount. $currentAmount = $this->getCurrentAmount($piggyBank); - if (1 === bccomp($currentAmount, (string)$piggyBank->target_amount) && 0 !== bccomp((string)$piggyBank->target_amount, '0')) { + if (1 === bccomp((string) $currentAmount, (string)$piggyBank->target_amount) && 0 !== bccomp((string)$piggyBank->target_amount, '0')) { Log::debug(sprintf('Current amount is %s, target amount is %s', $currentAmount, $piggyBank->target_amount)); - $difference = bcsub((string)$piggyBank->target_amount, $currentAmount); + $difference = bcsub((string)$piggyBank->target_amount, (string) $currentAmount); // an amount will be removed, create "negative" event: // Log::debug(sprintf('ChangedAmount: is triggered with difference "%s"', $difference)); diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index 06a41da184..576f6ec804 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Tag; +use Override; use Carbon\Carbon; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Factory\TagFactory; @@ -380,7 +381,7 @@ class TagRepository implements TagRepositoryInterface, UserGroupInterface return $tag->locations()->first(); } - #[\Override] + #[Override] public function periodCollection(Tag $tag, Carbon $start, Carbon $end): array { Log::debug(sprintf('periodCollection(#%d, %s, %s)', $tag->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); diff --git a/app/Services/Internal/Support/CreditRecalculateService.php b/app/Services/Internal/Support/CreditRecalculateService.php index eb6b33b8d4..66b5fc359c 100644 --- a/app/Services/Internal/Support/CreditRecalculateService.php +++ b/app/Services/Internal/Support/CreditRecalculateService.php @@ -276,66 +276,66 @@ class CreditRecalculateService if ($isSameAccount && $isCredit && $this->isWithdrawalIn($usedAmount, $type)) { // case 1 $usedAmount = app('steam')->positive($usedAmount); - return bcadd($leftOfDebt, $usedAmount); + return bcadd($leftOfDebt, (string) $usedAmount); // Log::debug(sprintf('Case 1 (withdrawal into credit liability): %s + %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); } if ($isSameAccount && $isCredit && $this->isWithdrawalOut($usedAmount, $type)) { // case 2 $usedAmount = app('steam')->positive($usedAmount); - return bcsub($leftOfDebt, $usedAmount); + return bcsub($leftOfDebt, (string) $usedAmount); // Log::debug(sprintf('Case 2 (withdrawal away from liability): %s - %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); } if ($isSameAccount && $isCredit && $this->isDepositOut($usedAmount, $type)) { // case 3 $usedAmount = app('steam')->positive($usedAmount); - return bcsub($leftOfDebt, $usedAmount); + return bcsub($leftOfDebt, (string) $usedAmount); // Log::debug(sprintf('Case 3 (deposit away from liability): %s - %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); } if ($isSameAccount && $isCredit && $this->isDepositIn($usedAmount, $type)) { // case 4 $usedAmount = app('steam')->positive($usedAmount); - return bcadd($leftOfDebt, $usedAmount); + return bcadd($leftOfDebt, (string) $usedAmount); // Log::debug(sprintf('Case 4 (deposit into credit liability): %s + %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); } if ($isSameAccount && $isCredit && $this->isTransferIn($usedAmount, $type)) { // case 5 $usedAmount = app('steam')->positive($usedAmount); - return bcadd($leftOfDebt, $usedAmount); + return bcadd($leftOfDebt, (string) $usedAmount); // Log::debug(sprintf('Case 5 (transfer into credit liability): %s + %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); } if ($isSameAccount && $isDebit && $this->isWithdrawalIn($usedAmount, $type)) { // case 6 $usedAmount = app('steam')->positive($usedAmount); - return bcsub($leftOfDebt, $usedAmount); + return bcsub($leftOfDebt, (string) $usedAmount); // Log::debug(sprintf('Case 6 (withdrawal into debit liability): %s - %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); } if ($isSameAccount && $isDebit && $this->isDepositOut($usedAmount, $type)) { // case 7 $usedAmount = app('steam')->positive($usedAmount); - return bcadd($leftOfDebt, $usedAmount); + return bcadd($leftOfDebt, (string) $usedAmount); // Log::debug(sprintf('Case 7 (deposit away from liability): %s + %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); } if ($isSameAccount && $isDebit && $this->isWithdrawalOut($usedAmount, $type)) { // case 8 $usedAmount = app('steam')->positive($usedAmount); - return bcadd($leftOfDebt, $usedAmount); + return bcadd($leftOfDebt, (string) $usedAmount); // Log::debug(sprintf('Case 8 (withdrawal away from liability): %s + %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); } if ($isSameAccount && $isDebit && $this->isTransferIn($usedAmount, $type)) { // case 9 $usedAmount = app('steam')->positive($usedAmount); - return bcsub($leftOfDebt, $usedAmount); + return bcsub($leftOfDebt, (string) $usedAmount); // 2024-10-05, #9225 this used to say you would owe more, but a transfer INTO a debit from wherever means you owe LESS. // Log::debug(sprintf('Case 9 (transfer into debit liability, means you owe LESS): %s - %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); } if ($isSameAccount && $isDebit && $this->isTransferOut($usedAmount, $type)) { // case 10 $usedAmount = app('steam')->positive($usedAmount); - return bcadd($leftOfDebt, $usedAmount); + return bcadd($leftOfDebt, (string) $usedAmount); // 2024-10-05, #9225 this used to say you would owe less, but a transfer OUT OF a debit from wherever means you owe MORE. // Log::debug(sprintf('Case 10 (transfer out of debit liability, means you owe MORE): %s + %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); } @@ -344,7 +344,7 @@ class CreditRecalculateService if (in_array($type, [TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::TRANSFER->value], true)) { $usedAmount = app('steam')->negative($usedAmount); - return bcadd($leftOfDebt, $usedAmount); + return bcadd($leftOfDebt, (string) $usedAmount); // Log::debug(sprintf('Case X (all other cases): %s + %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); } diff --git a/app/Support/Debug/Timer.php b/app/Support/Debug/Timer.php index b23e941a0c..5f2d6bc283 100644 --- a/app/Support/Debug/Timer.php +++ b/app/Support/Debug/Timer.php @@ -38,7 +38,7 @@ class Timer public static function getInstance(): self { - if (null === self::$instance) { + if (!self::$instance instanceof \FireflyIII\Support\Debug\Timer) { self::$instance = new self(); } diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index 3b9a708d88..98206c429e 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -724,7 +724,7 @@ trait PeriodOverview protected function saveGroupedAsStatistics(Model $model, Carbon $start, Carbon $end, string $type, array $array): void { unset($array['count']); - Log::debug(sprintf('saveGroupedAsStatistics(%s #%d, %s, %s, "%s", array(%d))', get_class($model), $model->id, $start->format('Y-m-d'), $end->format('Y-m-d'), $type, count($array))); + Log::debug(sprintf('saveGroupedAsStatistics(%s #%d, %s, %s, "%s", array(%d))', $model::class, $model->id, $start->format('Y-m-d'), $end->format('Y-m-d'), $type, count($array))); foreach ($array as $entry) { $this->periodStatisticRepo->saveStatistic($model, $entry['currency_id'], $start, $end, $type, $entry['count'], $entry['amount']); } @@ -899,7 +899,7 @@ trait PeriodOverview ]; - $return[$currencyId]['amount'] = bcadd($return[$currencyId]['amount'], $amount); + $return[$currencyId]['amount'] = bcadd((string) $return[$currencyId]['amount'], $amount); ++$return[$currencyId]['count']; ++$return['count']; } diff --git a/app/Support/JsonApi/Enrichments/CategoryEnrichment.php b/app/Support/JsonApi/Enrichments/CategoryEnrichment.php index 975227844f..b5ddd22c52 100644 --- a/app/Support/JsonApi/Enrichments/CategoryEnrichment.php +++ b/app/Support/JsonApi/Enrichments/CategoryEnrichment.php @@ -135,7 +135,7 @@ class CategoryEnrichment implements EnrichmentInterface private function collectTransactions(): void { - if (null !== $this->start && null !== $this->end) { + if ($this->start instanceof Carbon && $this->end instanceof Carbon) { /** @var OperationsRepositoryInterface $opsRepository */ $opsRepository = app(OperationsRepositoryInterface::class); $opsRepository->setUser($this->user); diff --git a/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php b/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php index e1afbe0042..7c6a8a8a5b 100644 --- a/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php +++ b/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php @@ -142,9 +142,9 @@ class PiggyBankEnrichment implements EnrichmentInterface 'current_amount' => Steam::bcround($row['current_amount'], $currency->decimal_places), 'pc_current_amount' => Steam::bcround($row['pc_current_amount'], $this->primaryCurrency->decimal_places), ]; - $meta['current_amount'] = bcadd($meta['current_amount'], $row['current_amount']); + $meta['current_amount'] = bcadd($meta['current_amount'], (string) $row['current_amount']); // only add pc_current_amount when the pc_current_amount is set - $meta['pc_current_amount'] = null === $row['pc_current_amount'] ? null : bcadd($meta['pc_current_amount'], $row['pc_current_amount']); + $meta['pc_current_amount'] = null === $row['pc_current_amount'] ? null : bcadd((string) $meta['pc_current_amount'], (string) $row['pc_current_amount']); } $meta['current_amount'] = Steam::bcround($meta['current_amount'], $currency->decimal_places); // only round this number when pc_current_amount is set. @@ -152,8 +152,8 @@ class PiggyBankEnrichment implements EnrichmentInterface // calculate left to save, only when there is a target amount. if (null !== $targetAmount) { - $meta['left_to_save'] = bcsub($meta['target_amount'], $meta['current_amount']); - $meta['pc_left_to_save'] = null === $meta['pc_target_amount'] ? null : bcsub($meta['pc_target_amount'], $meta['pc_current_amount']); + $meta['left_to_save'] = bcsub((string) $meta['target_amount'], (string) $meta['current_amount']); + $meta['pc_left_to_save'] = null === $meta['pc_target_amount'] ? null : bcsub((string) $meta['pc_target_amount'], (string) $meta['pc_current_amount']); } // get suggested per month. @@ -199,7 +199,7 @@ class PiggyBankEnrichment implements EnrichmentInterface 'pc_current_amount' => '0', ]; } - $this->amounts[$id][$accountId]['current_amount'] = bcadd($this->amounts[$id][$accountId]['current_amount'], (string)$item->current_amount); + $this->amounts[$id][$accountId]['current_amount'] = bcadd((string) $this->amounts[$id][$accountId]['current_amount'], (string)$item->current_amount); if (null !== $this->amounts[$id][$accountId]['pc_current_amount'] && null !== $item->native_current_amount) { $this->amounts[$id][$accountId]['pc_current_amount'] = bcadd($this->amounts[$id][$accountId]['pc_current_amount'], (string)$item->native_current_amount); } diff --git a/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php b/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php index 82f513a44e..0c77493667 100644 --- a/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php +++ b/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php @@ -197,7 +197,7 @@ trait RecalculatesAvailableBudgetsTrait // if not exists: $currentPeriod = Period::make($current, $currentEnd, precision: Precision::DAY(), boundaries: Boundaries::EXCLUDE_NONE()); $daily = $this->getDailyAmount($budgetLimit); - $amount = bcmul($daily, (string)$currentPeriod->length(), 12); + $amount = bcmul((string) $daily, (string)$currentPeriod->length(), 12); // no need to calculate if period is equal. if ($currentPeriod->equals($limitPeriod)) { diff --git a/app/Support/Report/Summarizer/TransactionSummarizer.php b/app/Support/Report/Summarizer/TransactionSummarizer.php index 5660a239f0..83f057c68c 100644 --- a/app/Support/Report/Summarizer/TransactionSummarizer.php +++ b/app/Support/Report/Summarizer/TransactionSummarizer.php @@ -193,7 +193,7 @@ class TransactionSummarizer ]; // add the data from the $field to the array. - $array[$key]['sum'] = bcadd($array[$key]['sum'], Steam::{$method}((string)($journal[$field] ?? '0'))); // @phpstan-ignore-line + $array[$key]['sum'] = bcadd($array[$key]['sum'], (string) Steam::{$method}((string)($journal[$field] ?? '0'))); // @phpstan-ignore-line Log::debug(sprintf('Field for transaction #%d is "%s" (%s). Sum: %s', $journal['transaction_group_id'], $currencyCode, $field, $array[$key]['sum'])); // also do foreign amount, but only when convertToPrimary is false (otherwise we have it already) @@ -211,7 +211,7 @@ class TransactionSummarizer 'currency_code' => $journal['foreign_currency_code'], 'currency_decimal_places' => $journal['foreign_currency_decimal_places'], ]; - $array[$key]['sum'] = bcadd($array[$key]['sum'], Steam::{$method}((string)$journal['foreign_amount'])); // @phpstan-ignore-line + $array[$key]['sum'] = bcadd($array[$key]['sum'], (string) Steam::{$method}((string)$journal['foreign_amount'])); // @phpstan-ignore-line } } diff --git a/app/Support/Singleton/PreferencesSingleton.php b/app/Support/Singleton/PreferencesSingleton.php index 287a964361..716eddc1e8 100644 --- a/app/Support/Singleton/PreferencesSingleton.php +++ b/app/Support/Singleton/PreferencesSingleton.php @@ -38,7 +38,7 @@ class PreferencesSingleton public static function getInstance(): self { - if (null === self::$instance) { + if (!self::$instance instanceof PreferencesSingleton) { self::$instance = new self(); } diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 46119a268a..71d5e788f3 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -756,7 +756,7 @@ class Steam $current = $converter->convert($currency, $primary, $date, $amount); Log::debug(sprintf('Convert %s %s to %s %s', $currency->code, $amount, $primary->code, $current)); } - $total = bcadd($current, $total); + $total = bcadd((string) $current, $total); } return $total; diff --git a/app/Transformers/PiggyBankEventTransformer.php b/app/Transformers/PiggyBankEventTransformer.php index a9724ddc57..a0a43ecc59 100644 --- a/app/Transformers/PiggyBankEventTransformer.php +++ b/app/Transformers/PiggyBankEventTransformer.php @@ -35,7 +35,7 @@ use FireflyIII\Support\Facades\Steam; */ class PiggyBankEventTransformer extends AbstractTransformer { - private TransactionCurrency $primaryCurrency; + private readonly TransactionCurrency $primaryCurrency; private bool $convertToPrimary = false; /** From 6743b3fe833c3ac19f3095df92f077566ba84a07 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 27 Sep 2025 06:01:14 +0200 Subject: [PATCH 55/91] Remove stats for other objects as well. --- app/Handlers/Events/StoredGroupEventHandler.php | 6 ++++++ app/Handlers/Events/UpdatedGroupEventHandler.php | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/app/Handlers/Events/StoredGroupEventHandler.php b/app/Handlers/Events/StoredGroupEventHandler.php index 9b4f75b4b3..531ef4a433 100644 --- a/app/Handlers/Events/StoredGroupEventHandler.php +++ b/app/Handlers/Events/StoredGroupEventHandler.php @@ -109,6 +109,12 @@ class StoredGroupEventHandler $dest = $journal->transactions()->where('amount', '>', '0')->first(); $repository->deleteStatisticsForModel($source->account, $journal->date); $repository->deleteStatisticsForModel($dest->account, $journal->date); + foreach($journal->categories as $category) { + $repository->deleteStatisticsForModel($category, $journal->date); + } + foreach($journal->tags as $tag) { + $repository->deleteStatisticsForModel($tag, $journal->date); + } } } diff --git a/app/Handlers/Events/UpdatedGroupEventHandler.php b/app/Handlers/Events/UpdatedGroupEventHandler.php index ec5ada671d..c8dc416b76 100644 --- a/app/Handlers/Events/UpdatedGroupEventHandler.php +++ b/app/Handlers/Events/UpdatedGroupEventHandler.php @@ -58,6 +58,13 @@ class UpdatedGroupEventHandler } + /** + * TODO duplicate + * + * @param UpdatedTransactionGroup $event + * + * @return void + */ private function removePeriodStatistics(UpdatedTransactionGroup $event): void { /** @var PeriodStatisticRepositoryInterface $repository */ @@ -69,6 +76,12 @@ class UpdatedGroupEventHandler $dest = $journal->transactions()->where('amount', '>', '0')->first(); $repository->deleteStatisticsForModel($source->account, $journal->date); $repository->deleteStatisticsForModel($dest->account, $journal->date); + foreach($journal->categories as $category) { + $repository->deleteStatisticsForModel($category, $journal->date); + } + foreach($journal->tags as $tag) { + $repository->deleteStatisticsForModel($tag, $journal->date); + } } } From 308abffb0bd4e8220e9374da78a161a98768dc50 Mon Sep 17 00:00:00 2001 From: JC5 Date: Sat, 27 Sep 2025 06:04:46 +0200 Subject: [PATCH 56/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-09-27?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .ci/php-cs-fixer/composer.lock | 12 +- app/Console/Commands/Tools/ApplyRules.php | 1 - .../Events/StoredGroupEventHandler.php | 4 +- .../Events/UpdatedGroupEventHandler.php | 8 +- .../Transaction/CreateController.php | 3 +- .../Category/CategoryRepository.php | 60 ++--- .../Category/OperationsRepository.php | 2 +- app/Repositories/Tag/TagRepository.php | 60 ++--- .../Tag/TagRepositoryInterface.php | 1 + app/Support/Debug/Timer.php | 2 +- .../Http/Controllers/PeriodOverview.php | 239 +++++++++--------- .../Singleton/PreferencesSingleton.php | 2 +- config/firefly.php | 4 +- 13 files changed, 197 insertions(+), 201 deletions(-) diff --git a/.ci/php-cs-fixer/composer.lock b/.ci/php-cs-fixer/composer.lock index 18ee8f03de..f397529f05 100644 --- a/.ci/php-cs-fixer/composer.lock +++ b/.ci/php-cs-fixer/composer.lock @@ -402,16 +402,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.88.0", + "version": "v3.88.2", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "f23469674ae50d40e398bfff8018911a2a2b0dbe" + "reference": "a8d15584bafb0f0d9d938827840060fd4a3ebc99" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/f23469674ae50d40e398bfff8018911a2a2b0dbe", - "reference": "f23469674ae50d40e398bfff8018911a2a2b0dbe", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/a8d15584bafb0f0d9d938827840060fd4a3ebc99", + "reference": "a8d15584bafb0f0d9d938827840060fd4a3ebc99", "shasum": "" }, "require": { @@ -494,7 +494,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.88.0" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.88.2" }, "funding": [ { @@ -502,7 +502,7 @@ "type": "github" } ], - "time": "2025-09-24T21:31:42+00:00" + "time": "2025-09-27T00:24:15+00:00" }, { "name": "psr/container", diff --git a/app/Console/Commands/Tools/ApplyRules.php b/app/Console/Commands/Tools/ApplyRules.php index c0ea4ed2db..6a42ac0990 100644 --- a/app/Console/Commands/Tools/ApplyRules.php +++ b/app/Console/Commands/Tools/ApplyRules.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Tools; -use Carbon\CarbonInterface; use Carbon\Carbon; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Console\Commands\VerifiesAccessToken; diff --git a/app/Handlers/Events/StoredGroupEventHandler.php b/app/Handlers/Events/StoredGroupEventHandler.php index 531ef4a433..50ce62f742 100644 --- a/app/Handlers/Events/StoredGroupEventHandler.php +++ b/app/Handlers/Events/StoredGroupEventHandler.php @@ -109,10 +109,10 @@ class StoredGroupEventHandler $dest = $journal->transactions()->where('amount', '>', '0')->first(); $repository->deleteStatisticsForModel($source->account, $journal->date); $repository->deleteStatisticsForModel($dest->account, $journal->date); - foreach($journal->categories as $category) { + foreach ($journal->categories as $category) { $repository->deleteStatisticsForModel($category, $journal->date); } - foreach($journal->tags as $tag) { + foreach ($journal->tags as $tag) { $repository->deleteStatisticsForModel($tag, $journal->date); } } diff --git a/app/Handlers/Events/UpdatedGroupEventHandler.php b/app/Handlers/Events/UpdatedGroupEventHandler.php index c8dc416b76..e1393a3355 100644 --- a/app/Handlers/Events/UpdatedGroupEventHandler.php +++ b/app/Handlers/Events/UpdatedGroupEventHandler.php @@ -60,10 +60,6 @@ class UpdatedGroupEventHandler /** * TODO duplicate - * - * @param UpdatedTransactionGroup $event - * - * @return void */ private function removePeriodStatistics(UpdatedTransactionGroup $event): void { @@ -76,10 +72,10 @@ class UpdatedGroupEventHandler $dest = $journal->transactions()->where('amount', '>', '0')->first(); $repository->deleteStatisticsForModel($source->account, $journal->date); $repository->deleteStatisticsForModel($dest->account, $journal->date); - foreach($journal->categories as $category) { + foreach ($journal->categories as $category) { $repository->deleteStatisticsForModel($category, $journal->date); } - foreach($journal->tags as $tag) { + foreach ($journal->tags as $tag) { $repository->deleteStatisticsForModel($tag, $journal->date); } } diff --git a/app/Http/Controllers/Transaction/CreateController.php b/app/Http/Controllers/Transaction/CreateController.php index 21c573565e..c69ddfc2ce 100644 --- a/app/Http/Controllers/Transaction/CreateController.php +++ b/app/Http/Controllers/Transaction/CreateController.php @@ -115,7 +115,8 @@ class CreateController extends Controller $preFilled = session()->has('preFilled') ? session('preFilled') : []; $subTitle = (string) trans(sprintf('breadcrumbs.create_%s', strtolower((string) $objectType))); $subTitleIcon = 'fa-plus'; - /** @var array|null $optionalFields */ + + /** @var null|array $optionalFields */ $optionalFields = Preferences::get('transaction_journal_optional_fields', [])->data; $allowedOpposingTypes = config('firefly.allowed_opposing_types'); $accountToTypes = config('firefly.account_to_transaction'); diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index f644c57070..894690e9e6 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -364,37 +364,37 @@ class CategoryRepository implements CategoryRepositoryInterface, UserGroupInterf Log::debug(sprintf('periodCollection(#%d, %s, %s)', $category->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); return $category->transactionJournals() - ->leftJoin('transactions','transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') - ->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id') - ->leftJoin('transaction_currencies as foreign_currencies', 'foreign_currencies.id', '=', 'transactions.foreign_currency_id') - ->where('transaction_journals.date', '>=', $start) - ->where('transaction_journals.date', '<=', $end) - ->where('transactions.amount', '>', 0) - ->get([ - // currencies - 'transaction_currencies.id as currency_id', - 'transaction_currencies.code as currency_code', - 'transaction_currencies.name as currency_name', - 'transaction_currencies.symbol as currency_symbol', - 'transaction_currencies.decimal_places as currency_decimal_places', + ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') + ->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id') + ->leftJoin('transaction_currencies as foreign_currencies', 'foreign_currencies.id', '=', 'transactions.foreign_currency_id') + ->where('transaction_journals.date', '>=', $start) + ->where('transaction_journals.date', '<=', $end) + ->where('transactions.amount', '>', 0) + ->get([ + // currencies + 'transaction_currencies.id as currency_id', + 'transaction_currencies.code as currency_code', + 'transaction_currencies.name as currency_name', + 'transaction_currencies.symbol as currency_symbol', + 'transaction_currencies.decimal_places as currency_decimal_places', - // foreign - 'foreign_currencies.id as foreign_currency_id', - 'foreign_currencies.code as foreign_currency_code', - 'foreign_currencies.name as foreign_currency_name', - 'foreign_currencies.symbol as foreign_currency_symbol', - 'foreign_currencies.decimal_places as foreign_currency_decimal_places', + // foreign + 'foreign_currencies.id as foreign_currency_id', + 'foreign_currencies.code as foreign_currency_code', + 'foreign_currencies.name as foreign_currency_name', + 'foreign_currencies.symbol as foreign_currency_symbol', + 'foreign_currencies.decimal_places as foreign_currency_decimal_places', - // fields - 'transaction_journals.date', - 'transaction_types.type', - 'transaction_journals.transaction_currency_id', - 'transactions.amount', - 'transactions.native_amount as pc_amount', - 'transactions.foreign_amount', - ]) - ->toArray() - ; + // fields + 'transaction_journals.date', + 'transaction_types.type', + 'transaction_journals.transaction_currency_id', + 'transactions.amount', + 'transactions.native_amount as pc_amount', + 'transactions.foreign_amount', + ]) + ->toArray() + ; } } diff --git a/app/Repositories/Category/OperationsRepository.php b/app/Repositories/Category/OperationsRepository.php index d88bc16d0b..a1fe709f4c 100644 --- a/app/Repositories/Category/OperationsRepository.php +++ b/app/Repositories/Category/OperationsRepository.php @@ -510,7 +510,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn $summarizer->setConvertToPrimary($convertToPrimary); // filter $journals by range AND currency if it is present. - $expenses = array_filter($expenses, static fn(array $expense): bool => $expense['category_id'] === $category->id); + $expenses = array_filter($expenses, static fn (array $expense): bool => $expense['category_id'] === $category->id); return $summarizer->groupByCurrencyId($expenses, $method, false); } diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index 576f6ec804..eb50e2ef8f 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -387,37 +387,37 @@ class TagRepository implements TagRepositoryInterface, UserGroupInterface Log::debug(sprintf('periodCollection(#%d, %s, %s)', $tag->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); return $tag->transactionJournals() - ->leftJoin('transactions','transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') - ->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id') - ->leftJoin('transaction_currencies as foreign_currencies', 'foreign_currencies.id', '=', 'transactions.foreign_currency_id') - ->where('transaction_journals.date', '>=', $start) - ->where('transaction_journals.date', '<=', $end) - ->where('transactions.amount', '>', 0) - ->get([ - // currencies - 'transaction_currencies.id as currency_id', - 'transaction_currencies.code as currency_code', - 'transaction_currencies.name as currency_name', - 'transaction_currencies.symbol as currency_symbol', - 'transaction_currencies.decimal_places as currency_decimal_places', + ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') + ->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id') + ->leftJoin('transaction_currencies as foreign_currencies', 'foreign_currencies.id', '=', 'transactions.foreign_currency_id') + ->where('transaction_journals.date', '>=', $start) + ->where('transaction_journals.date', '<=', $end) + ->where('transactions.amount', '>', 0) + ->get([ + // currencies + 'transaction_currencies.id as currency_id', + 'transaction_currencies.code as currency_code', + 'transaction_currencies.name as currency_name', + 'transaction_currencies.symbol as currency_symbol', + 'transaction_currencies.decimal_places as currency_decimal_places', - // foreign - 'foreign_currencies.id as foreign_currency_id', - 'foreign_currencies.code as foreign_currency_code', - 'foreign_currencies.name as foreign_currency_name', - 'foreign_currencies.symbol as foreign_currency_symbol', - 'foreign_currencies.decimal_places as foreign_currency_decimal_places', + // foreign + 'foreign_currencies.id as foreign_currency_id', + 'foreign_currencies.code as foreign_currency_code', + 'foreign_currencies.name as foreign_currency_name', + 'foreign_currencies.symbol as foreign_currency_symbol', + 'foreign_currencies.decimal_places as foreign_currency_decimal_places', - // fields - 'transaction_journals.date', - 'transaction_types.type', - 'transaction_journals.transaction_currency_id', - 'transactions.amount', - 'transactions.native_amount as pc_amount', - 'transactions.foreign_amount', - ]) - ->toArray() - ; + // fields + 'transaction_journals.date', + 'transaction_types.type', + 'transaction_journals.transaction_currency_id', + 'transactions.amount', + 'transactions.native_amount as pc_amount', + 'transactions.foreign_amount', + ]) + ->toArray() + ; } } diff --git a/app/Repositories/Tag/TagRepositoryInterface.php b/app/Repositories/Tag/TagRepositoryInterface.php index 18e45fb9ce..5a53efe7fe 100644 --- a/app/Repositories/Tag/TagRepositoryInterface.php +++ b/app/Repositories/Tag/TagRepositoryInterface.php @@ -50,6 +50,7 @@ interface TagRepositoryInterface * This method destroys a tag. */ public function destroy(Tag $tag): bool; + public function periodCollection(Tag $tag, Carbon $start, Carbon $end): array; /** diff --git a/app/Support/Debug/Timer.php b/app/Support/Debug/Timer.php index 5f2d6bc283..31119addc4 100644 --- a/app/Support/Debug/Timer.php +++ b/app/Support/Debug/Timer.php @@ -38,7 +38,7 @@ class Timer public static function getInstance(): self { - if (!self::$instance instanceof \FireflyIII\Support\Debug\Timer) { + if (!self::$instance instanceof self) { self::$instance = new self(); } diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index 98206c429e..dcf3d574ad 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -92,16 +92,16 @@ trait PeriodOverview protected function getAccountPeriodOverview(Account $account, Carbon $start, Carbon $end): array { Log::debug(sprintf('Now in getAccountPeriodOverview(#%d, %s %s)', $account->id, $start->format('Y-m-d H:i:s.u'), $end->format('Y-m-d H:i:s.u'))); - $this->accountRepository = app(AccountRepositoryInterface::class); + $this->accountRepository = app(AccountRepositoryInterface::class); $this->accountRepository->setUser($account->user); $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); $range = Navigation::getViewRange(true); - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); - $this->statistics = $this->periodStatisticRepo->allInRangeForModel($account, $start, $end); + $dates = Navigation::blockPeriods($start, $end, $range); + [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); + $this->statistics = $this->periodStatisticRepo->allInRangeForModel($account, $start, $end); // TODO needs to be re-arranged: // get all period stats for entire range. @@ -109,7 +109,7 @@ trait PeriodOverview // create new ones, or use collected. - $entries = []; + $entries = []; Log::debug(sprintf('Count of loops: %d', count($dates))); foreach ($dates as $currentDate) { $entries[] = $this->getSingleAccountPeriod($account, $currentDate['period'], $currentDate['start'], $currentDate['end']); @@ -145,18 +145,18 @@ trait PeriodOverview */ protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array { - $this->categoryRepository = app(CategoryRepositoryInterface::class); + $this->categoryRepository = app(CategoryRepositoryInterface::class); $this->categoryRepository->setUser($category->user); $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); - $range = Navigation::getViewRange(true); - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + $range = Navigation::getViewRange(true); + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); - $this->statistics = $this->periodStatisticRepo->allInRangeForModel($category, $start, $end); + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; + [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); + $this->statistics = $this->periodStatisticRepo->allInRangeForModel($category, $start, $end); Log::debug(sprintf('Count of loops: %d', count($dates))); @@ -176,11 +176,11 @@ trait PeriodOverview */ protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty($this->convertToPrimary); @@ -191,28 +191,28 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // get all expenses without a budget. /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end)->withoutBudget()->withAccountInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $journals = $collector->getExtractedJournals(); + $journals = $collector->getExtractedJournals(); foreach ($dates as $currentDate) { $set = $this->filterJournalsByDate($journals, $currentDate['start'], $currentDate['end']); $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'title' => $title, - 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($set), - 'spent' => $this->groupByCurrency($set), - 'earned' => [], - 'transferred_away' => [], - 'transferred_in' => [], - ]; + 'title' => $title, + 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($set), + 'spent' => $this->groupByCurrency($set), + 'earned' => [], + 'transferred_away' => [], + 'transferred_in' => [], + ]; } $cache->store($entries); @@ -229,38 +229,38 @@ trait PeriodOverview protected function getNoCategoryPeriodOverview(Carbon $theDate): array { Log::debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d'))); - $range = Navigation::getViewRange(true); - $first = $this->journalRepos->firstNull(); - $start = null === $first ? new Carbon() : $first->date; - $end = clone $theDate; - $end = Navigation::endOfPeriod($end, $range); + $range = Navigation::getViewRange(true); + $first = $this->journalRepos->firstNull(); + $start = null === $first ? new Carbon() : $first->date; + $end = clone $theDate; + $end = Navigation::endOfPeriod($end, $range); Log::debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d'))); Log::debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d'))); // properties for cache - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); @@ -274,20 +274,19 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'title' => $title, - 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'title' => $title, + 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } Log::debug('End of loops'); return $entries; } - protected function getSingleAccountPeriod(Account $account, string $period, Carbon $start, Carbon $end): array { Log::debug(sprintf('Now in getSingleAccountPeriod(#%d, %s %s)', $account->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); @@ -299,7 +298,7 @@ trait PeriodOverview ]; $this->transactions = []; foreach ($types as $type) { - $set = $this->getSingleAccountPeriodByType($account, $start, $end, $type); + $set = $this->getSingleAccountPeriodByType($account, $start, $end, $type); $return['total_transactions'] += $set['count']; unset($set['count']); $return[$type] = $set; @@ -319,7 +318,7 @@ trait PeriodOverview ]; $this->transactions = []; foreach ($types as $type) { - $set = $this->getSingleCategoryPeriodByType($category, $start, $end, $type); + $set = $this->getSingleCategoryPeriodByType($category, $start, $end, $type); $return['total_transactions'] += $set['count']; unset($set['count']); $return[$type] = $set; @@ -339,7 +338,7 @@ trait PeriodOverview ]; $this->transactions = []; foreach ($types as $type) { - $set = $this->getSingleTagPeriodByType($tag, $start, $end, $type); + $set = $this->getSingleTagPeriodByType($tag, $start, $end, $type); $return['total_transactions'] += $set['count']; unset($set['count']); $return[$type] = $set; @@ -413,15 +412,15 @@ trait PeriodOverview return $grouped; } - $grouped = [ + $grouped = [ 'count' => 0, ]; /** @var PeriodStatistic $statistic */ foreach ($statistics as $statistic) { - $id = (int)$statistic->transaction_currency_id; - $currency = Amount::getTransactionCurrencyById($id); - $grouped[$id] = [ + $id = (int)$statistic->transaction_currency_id; + $currency = Amount::getTransactionCurrencyById($id); + $grouped[$id] = [ 'amount' => (string)$statistic->amount, 'count' => (int)$statistic->count, 'currency_id' => $currency->id, @@ -481,15 +480,15 @@ trait PeriodOverview return $grouped; } - $grouped = [ + $grouped = [ 'count' => 0, ]; /** @var PeriodStatistic $statistic */ foreach ($statistics as $statistic) { - $id = (int)$statistic->transaction_currency_id; - $currency = Amount::getTransactionCurrencyById($id); - $grouped[$id] = [ + $id = (int)$statistic->transaction_currency_id; + $currency = Amount::getTransactionCurrencyById($id); + $grouped[$id] = [ 'amount' => (string)$statistic->amount, 'count' => (int)$statistic->count, 'currency_id' => $currency->id, @@ -549,15 +548,15 @@ trait PeriodOverview return $grouped; } - $grouped = [ + $grouped = [ 'count' => 0, ]; /** @var PeriodStatistic $statistic */ foreach ($statistics as $statistic) { - $id = (int)$statistic->transaction_currency_id; - $currency = Amount::getTransactionCurrencyById($id); - $grouped[$id] = [ + $id = (int)$statistic->transaction_currency_id; + $currency = Amount::getTransactionCurrencyById($id); + $grouped[$id] = [ 'amount' => (string)$statistic->amount, 'count' => (int)$statistic->count, 'currency_id' => $currency->id, @@ -579,18 +578,18 @@ trait PeriodOverview */ protected function getTagPeriodOverview(Tag $tag, Carbon $start, Carbon $end): array // period overview for tags. { - $this->tagRepository = app(TagRepositoryInterface::class); + $this->tagRepository = app(TagRepositoryInterface::class); $this->tagRepository->setUser($tag->user); $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); - $range = Navigation::getViewRange(true); - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + $range = Navigation::getViewRange(true); + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); - $this->statistics = $this->periodStatisticRepo->allInRangeForModel($tag, $start, $end); + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; + [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); + $this->statistics = $this->periodStatisticRepo->allInRangeForModel($tag, $start, $end); Log::debug(sprintf('Count of loops: %d', count($dates))); @@ -601,41 +600,41 @@ trait PeriodOverview return $entries; - $range = Navigation::getViewRange(true); - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + $range = Navigation::getViewRange(true); + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTag($tag); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); - $transferSet = $collector->getExtractedJournals(); + $transferSet = $collector->getExtractedJournals(); // filer all of them: - $earnedSet = $this->filterJournalsByTag($earnedSet, $tag); - $spentSet = $this->filterJournalsByTag($spentSet, $tag); - $transferSet = $this->filterJournalsByTag($transferSet, $tag); + $earnedSet = $this->filterJournalsByTag($earnedSet, $tag); + $spentSet = $this->filterJournalsByTag($spentSet, $tag); + $transferSet = $this->filterJournalsByTag($transferSet, $tag); foreach ($dates as $currentDate) { $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); @@ -644,17 +643,17 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'transactions' => 0, - 'title' => $title, - 'route' => route( - 'tags.show', - [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] - ), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'transactions' => 0, + 'title' => $title, + 'route' => route( + 'tags.show', + [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] + ), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } return $entries; @@ -665,12 +664,12 @@ trait PeriodOverview */ protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); - $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); + $range = Navigation::getViewRange(true); + $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; // properties for cache - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('transactions-period-entries'); @@ -680,16 +679,16 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - $spent = []; - $earned = []; - $transferred = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; + $spent = []; + $earned = []; + $transferred = []; // collect all journals in this period (regardless of type) - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTypes($types)->setRange($start, $end); - $genericSet = $collector->getExtractedJournals(); - $loops = 0; + $genericSet = $collector->getExtractedJournals(); + $loops = 0; foreach ($dates as $currentDate) { $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); @@ -707,14 +706,14 @@ trait PeriodOverview } } $entries[] - = [ - 'title' => $title, - 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + = [ + 'title' => $title, + 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; ++$loops; } @@ -755,7 +754,7 @@ trait PeriodOverview { $return = []; foreach ($set as $entry) { - $found = false; + $found = false; /** @var array $localTag */ foreach ($entry['tags'] as $localTag) { @@ -864,13 +863,13 @@ trait PeriodOverview /** @var array $journal */ foreach ($journals as $journal) { - $currencyId = (int)$journal['currency_id']; - $currencyCode = $journal['currency_code']; - $currencyName = $journal['currency_name']; - $currencySymbol = $journal['currency_symbol']; - $currencyDecimalPlaces = $journal['currency_decimal_places']; - $foreignCurrencyId = $journal['foreign_currency_id']; - $amount = $journal['amount'] ?? '0'; + $currencyId = (int)$journal['currency_id']; + $currencyCode = $journal['currency_code']; + $currencyName = $journal['currency_name']; + $currencySymbol = $journal['currency_symbol']; + $currencyDecimalPlaces = $journal['currency_decimal_places']; + $foreignCurrencyId = $journal['foreign_currency_id']; + $amount = $journal['amount'] ?? '0'; if ($this->convertToPrimary && $currencyId !== $this->primaryCurrency->id && $foreignCurrencyId !== $this->primaryCurrency->id) { $amount = $journal['pc_amount'] ?? '0'; diff --git a/app/Support/Singleton/PreferencesSingleton.php b/app/Support/Singleton/PreferencesSingleton.php index 716eddc1e8..646e1c60bf 100644 --- a/app/Support/Singleton/PreferencesSingleton.php +++ b/app/Support/Singleton/PreferencesSingleton.php @@ -38,7 +38,7 @@ class PreferencesSingleton public static function getInstance(): self { - if (!self::$instance instanceof PreferencesSingleton) { + if (!self::$instance instanceof self) { self::$instance = new self(); } diff --git a/config/firefly.php b/config/firefly.php index c990ec4a8e..da4d0d6233 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -78,8 +78,8 @@ return [ 'running_balance_column' => env('USE_RUNNING_BALANCE', false), // see cer.php for exchange rates feature flag. ], - 'version' => 'develop/2025-09-26', - 'build_time' => 1758914637, + 'version' => 'develop/2025-09-27', + 'build_time' => 1758945787, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 27, From 2cc8568077a3b9275bbc6d0215106abd2b0a42c3 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 27 Sep 2025 06:40:56 +0200 Subject: [PATCH 57/91] Clean up code. --- .../Http/Controllers/PeriodOverview.php | 498 ++++-------------- 1 file changed, 111 insertions(+), 387 deletions(-) diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index dcf3d574ad..e5b020e772 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -44,6 +44,7 @@ use FireflyIII\Support\Facades\Steam; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; +use Illuminate\Support\Str; /** * Trait PeriodOverview. @@ -76,7 +77,7 @@ trait PeriodOverview { protected AccountRepositoryInterface $accountRepository; protected CategoryRepositoryInterface $categoryRepository; - protected TagRepositoryInterface $tagRepository; + protected TagRepositoryInterface $tagRepository; protected JournalRepositoryInterface $journalRepos; protected PeriodStatisticRepositoryInterface $periodStatisticRepo; private Collection $statistics; // temp data holder @@ -92,27 +93,21 @@ trait PeriodOverview protected function getAccountPeriodOverview(Account $account, Carbon $start, Carbon $end): array { Log::debug(sprintf('Now in getAccountPeriodOverview(#%d, %s %s)', $account->id, $start->format('Y-m-d H:i:s.u'), $end->format('Y-m-d H:i:s.u'))); - $this->accountRepository = app(AccountRepositoryInterface::class); + $this->accountRepository = app(AccountRepositoryInterface::class); $this->accountRepository->setUser($account->user); $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); $range = Navigation::getViewRange(true); - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); - $this->statistics = $this->periodStatisticRepo->allInRangeForModel($account, $start, $end); + $dates = Navigation::blockPeriods($start, $end, $range); + [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); + $this->statistics = $this->periodStatisticRepo->allInRangeForModel($account, $start, $end); - // TODO needs to be re-arranged: - // get all period stats for entire range. - // loop blocks, an loop the types, and select the missing ones. - // create new ones, or use collected. - - - $entries = []; + $entries = []; Log::debug(sprintf('Count of loops: %d', count($dates))); foreach ($dates as $currentDate) { - $entries[] = $this->getSingleAccountPeriod($account, $currentDate['period'], $currentDate['start'], $currentDate['end']); + $entries[] = $this->getSingleModelPeriod($account, $currentDate['period'], $currentDate['start'], $currentDate['end']); } Log::debug('End of getAccountPeriodOverview()'); @@ -145,23 +140,23 @@ trait PeriodOverview */ protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array { - $this->categoryRepository = app(CategoryRepositoryInterface::class); + $this->categoryRepository = app(CategoryRepositoryInterface::class); $this->categoryRepository->setUser($category->user); $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); - $range = Navigation::getViewRange(true); - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + $range = Navigation::getViewRange(true); + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); - $this->statistics = $this->periodStatisticRepo->allInRangeForModel($category, $start, $end); + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; + [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); + $this->statistics = $this->periodStatisticRepo->allInRangeForModel($category, $start, $end); Log::debug(sprintf('Count of loops: %d', count($dates))); foreach ($dates as $currentDate) { - $entries[] = $this->getSingleCategoryPeriod($category, $currentDate['period'], $currentDate['start'], $currentDate['end']); + $entries[] = $this->getSingleModelPeriod($category, $currentDate['period'], $currentDate['start'], $currentDate['end']); } return $entries; @@ -176,11 +171,11 @@ trait PeriodOverview */ protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty($this->convertToPrimary); @@ -191,28 +186,28 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // get all expenses without a budget. /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end)->withoutBudget()->withAccountInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $journals = $collector->getExtractedJournals(); + $journals = $collector->getExtractedJournals(); foreach ($dates as $currentDate) { $set = $this->filterJournalsByDate($journals, $currentDate['start'], $currentDate['end']); $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'title' => $title, - 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($set), - 'spent' => $this->groupByCurrency($set), - 'earned' => [], - 'transferred_away' => [], - 'transferred_in' => [], - ]; + 'title' => $title, + 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($set), + 'spent' => $this->groupByCurrency($set), + 'earned' => [], + 'transferred_away' => [], + 'transferred_in' => [], + ]; } $cache->store($entries); @@ -229,38 +224,38 @@ trait PeriodOverview protected function getNoCategoryPeriodOverview(Carbon $theDate): array { Log::debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d'))); - $range = Navigation::getViewRange(true); - $first = $this->journalRepos->firstNull(); - $start = null === $first ? new Carbon() : $first->date; - $end = clone $theDate; - $end = Navigation::endOfPeriod($end, $range); + $range = Navigation::getViewRange(true); + $first = $this->journalRepos->firstNull(); + $start = null === $first ? new Carbon() : $first->date; + $end = clone $theDate; + $end = Navigation::endOfPeriod($end, $range); Log::debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d'))); Log::debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d'))); // properties for cache - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); + $earnedSet = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); + $spentSet = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); @@ -274,31 +269,31 @@ trait PeriodOverview $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'title' => $title, - 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'title' => $title, + 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } Log::debug('End of loops'); return $entries; } - protected function getSingleAccountPeriod(Account $account, string $period, Carbon $start, Carbon $end): array + protected function getSingleModelPeriod(Model $model, string $period, Carbon $start, Carbon $end): array { - Log::debug(sprintf('Now in getSingleAccountPeriod(#%d, %s %s)', $account->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); + Log::debug(sprintf('Now in getSingleModelPeriod(%s #%d, %s %s)', $model::class, $model->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); $types = ['spent', 'earned', 'transferred_in', 'transferred_away']; $return = [ 'title' => Navigation::periodShow($start, $period), - 'route' => route('accounts.show', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]), + 'route' => route(sprintf('%s.show', strtolower(Str::plural(class_basename($model)))), [$model->id, $start->format('Y-m-d'), $end->format('Y-m-d')]), 'total_transactions' => 0, ]; $this->transactions = []; foreach ($types as $type) { - $set = $this->getSingleAccountPeriodByType($account, $start, $end, $type); + $set = $this->getSingleModelPeriodByType($model, $start, $end, $type); $return['total_transactions'] += $set['count']; unset($set['count']); $return[$type] = $set; @@ -307,45 +302,6 @@ trait PeriodOverview return $return; } - protected function getSingleCategoryPeriod(Category $category, string $period, Carbon $start, Carbon $end): array - { - Log::debug(sprintf('Now in getSingleCategoryPeriod(#%d, %s %s)', $category->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); - $types = ['spent', 'earned', 'transferred_in', 'transferred_away']; - $return = [ - 'title' => Navigation::periodShow($start, $period), - 'route' => route('categories.show', [$category->id, $start->format('Y-m-d'), $end->format('Y-m-d')]), - 'total_transactions' => 0, - ]; - $this->transactions = []; - foreach ($types as $type) { - $set = $this->getSingleCategoryPeriodByType($category, $start, $end, $type); - $return['total_transactions'] += $set['count']; - unset($set['count']); - $return[$type] = $set; - } - - return $return; - } - - protected function getSingleTagPeriod(Tag $tag, string $period, Carbon $start, Carbon $end): array - { - Log::debug(sprintf('Now in getSingleTagPeriod(#%d, %s %s)', $tag->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); - $types = ['spent', 'earned', 'transferred_in', 'transferred_away']; - $return = [ - 'title' => Navigation::periodShow($start, $period), - 'route' => route('tags.show', [$tag->id, $start->format('Y-m-d'), $end->format('Y-m-d')]), - 'total_transactions' => 0, - ]; - $this->transactions = []; - foreach ($types as $type) { - $set = $this->getSingleTagPeriodByType($tag, $start, $end, $type); - $return['total_transactions'] += $set['count']; - unset($set['count']); - $return[$type] = $set; - } - - return $return; - } protected function filterStatistics(Carbon $start, Carbon $end, string $type): Collection { @@ -368,83 +324,30 @@ trait PeriodOverview ); } - protected function getSingleAccountPeriodByType(Account $account, Carbon $start, Carbon $end, string $type): array + + + protected function getSingleModelPeriodByType(Model $model, Carbon $start, Carbon $end, string $type): array { - Log::debug(sprintf('Now in getSingleAccountPeriodByType(#%d, %s %s, %s)', $account->id, $start->format('Y-m-d'), $end->format('Y-m-d'), $type)); + Log::debug(sprintf('Now in getSingleModelPeriodByType(%s #%d, %s %s, %s)', $model::class, $model->id, $start->format('Y-m-d'), $end->format('Y-m-d'), $type)); $statistics = $this->filterStatistics($start, $end, $type); // nothing found, regenerate them. if (0 === $statistics->count()) { Log::debug(sprintf('Found nothing in this period for type "%s"', $type)); if (0 === count($this->transactions)) { - $this->transactions = $this->accountRepository->periodCollection($account, $start, $end); - } - - switch ($type) { - default: - throw new FireflyException(sprintf('Cannot deal with account period type %s', $type)); - - case 'spent': - $result = $this->filterTransactionsByType(TransactionTypeEnum::WITHDRAWAL, $start, $end); - - break; - - case 'earned': - $result = $this->filterTransactionsByType(TransactionTypeEnum::DEPOSIT, $start, $end); - - break; - - case 'transferred_in': - $result = $this->filterTransfers('in', $start, $end); - - break; - - case 'transferred_away': - $result = $this->filterTransfers('away', $start, $end); - - break; - } - // each result must be grouped by currency, then saved as period statistic. - Log::debug(sprintf('Going to group %d found journal(s)', count($result))); - $grouped = $this->groupByCurrency($result); - - $this->saveGroupedAsStatistics($account, $start, $end, $type, $grouped); - - return $grouped; - } - $grouped = [ - 'count' => 0, - ]; - - /** @var PeriodStatistic $statistic */ - foreach ($statistics as $statistic) { - $id = (int)$statistic->transaction_currency_id; - $currency = Amount::getTransactionCurrencyById($id); - $grouped[$id] = [ - 'amount' => (string)$statistic->amount, - 'count' => (int)$statistic->count, - 'currency_id' => $currency->id, - 'currency_name' => $currency->name, - 'currency_code' => $currency->code, - 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => $currency->decimal_places, - ]; - $grouped['count'] += (int)$statistic->count; - } - - return $grouped; - } - - protected function getSingleCategoryPeriodByType(Category $category, Carbon $start, Carbon $end, string $type): array - { - Log::debug(sprintf('Now in getSingleCategoryPeriodByType(#%d, %s %s, %s)', $category->id, $start->format('Y-m-d'), $end->format('Y-m-d'), $type)); - $statistics = $this->filterStatistics($start, $end, $type); - - // nothing found, regenerate them. - if (0 === $statistics->count()) { - Log::debug(sprintf('Found nothing in this period for type "%s"', $type)); - if (0 === count($this->transactions)) { - $this->transactions = $this->categoryRepository->periodCollection($category, $start, $end); + switch ($model::class) { + default: + throw new FireflyException(sprintf('Cannot deal with model of type "%s"', $model::class)); + case Category::class: + $this->transactions = $this->categoryRepository->periodCollection($model, $start, $end); + break; + case Account::class: + $this->transactions = $this->accountRepository->periodCollection($model, $start, $end); + break; + case Tag::class: + $this->transactions = $this->tagRepository->periodCollection($model, $start, $end); + break; + } } switch ($type) { @@ -476,19 +379,19 @@ trait PeriodOverview Log::debug(sprintf('Going to group %d found journal(s)', count($result))); $grouped = $this->groupByCurrency($result); - $this->saveGroupedAsStatistics($category, $start, $end, $type, $grouped); + $this->saveGroupedAsStatistics($model, $start, $end, $type, $grouped); return $grouped; } - $grouped = [ + $grouped = [ 'count' => 0, ]; /** @var PeriodStatistic $statistic */ foreach ($statistics as $statistic) { - $id = (int)$statistic->transaction_currency_id; - $currency = Amount::getTransactionCurrencyById($id); - $grouped[$id] = [ + $id = (int)$statistic->transaction_currency_id; + $currency = Amount::getTransactionCurrencyById($id); + $grouped[$id] = [ 'amount' => (string)$statistic->amount, 'count' => (int)$statistic->count, 'currency_id' => $currency->id, @@ -503,73 +406,6 @@ trait PeriodOverview return $grouped; } - protected function getSingleTagPeriodByType(Tag $tag, Carbon $start, Carbon $end, string $type): array - { - Log::debug(sprintf('Now in getSingleTagPeriodByType(#%d, %s %s, %s)', $tag->id, $start->format('Y-m-d'), $end->format('Y-m-d'), $type)); - $statistics = $this->filterStatistics($start, $end, $type); - - // nothing found, regenerate them. - if (0 === $statistics->count()) { - Log::debug(sprintf('Found nothing in this period for type "%s"', $type)); - if (0 === count($this->transactions)) { - $this->transactions = $this->tagRepository->periodCollection($tag, $start, $end); - } - - switch ($type) { - default: - throw new FireflyException(sprintf('Cannot deal with tag period type %s', $type)); - - case 'spent': - - $result = $this->filterTransactionsByType(TransactionTypeEnum::WITHDRAWAL, $start, $end); - - break; - - case 'earned': - $result = $this->filterTransactionsByType(TransactionTypeEnum::DEPOSIT, $start, $end); - - break; - - case 'transferred_in': - $result = $this->filterTransfers('in', $start, $end); - - break; - - case 'transferred_away': - $result = $this->filterTransfers('away', $start, $end); - - break; - } - // each result must be grouped by currency, then saved as period statistic. - Log::debug(sprintf('Going to group %d found journal(s)', count($result))); - $grouped = $this->groupByCurrency($result); - - $this->saveGroupedAsStatistics($tag, $start, $end, $type, $grouped); - - return $grouped; - } - $grouped = [ - 'count' => 0, - ]; - - /** @var PeriodStatistic $statistic */ - foreach ($statistics as $statistic) { - $id = (int)$statistic->transaction_currency_id; - $currency = Amount::getTransactionCurrencyById($id); - $grouped[$id] = [ - 'amount' => (string)$statistic->amount, - 'count' => (int)$statistic->count, - 'currency_id' => $currency->id, - 'currency_name' => $currency->name, - 'currency_code' => $currency->code, - 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => $currency->decimal_places, - ]; - $grouped['count'] += (int)$statistic->count; - } - - return $grouped; - } /** * This shows a period overview for a tag. It goes back in time and lists all relevant transactions and sums. @@ -578,82 +414,23 @@ trait PeriodOverview */ protected function getTagPeriodOverview(Tag $tag, Carbon $start, Carbon $end): array // period overview for tags. { - $this->tagRepository = app(TagRepositoryInterface::class); + $this->tagRepository = app(TagRepositoryInterface::class); $this->tagRepository->setUser($tag->user); $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); - $range = Navigation::getViewRange(true); - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + $range = Navigation::getViewRange(true); + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); - $this->statistics = $this->periodStatisticRepo->allInRangeForModel($tag, $start, $end); + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; + [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); + $this->statistics = $this->periodStatisticRepo->allInRangeForModel($tag, $start, $end); Log::debug(sprintf('Count of loops: %d', count($dates))); foreach ($dates as $currentDate) { - $entries[] = $this->getSingleTagPeriod($tag, $currentDate['period'], $currentDate['start'], $currentDate['end']); - } - - return $entries; - - - $range = Navigation::getViewRange(true); - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; - - /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - - // collect all expenses in this period: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->setTag($tag); - $collector->setRange($start, $end); - $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); - - // collect all income in this period: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->setTag($tag); - $collector->setRange($start, $end); - $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); - - // collect all transfers in this period: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->setTag($tag); - $collector->setRange($start, $end); - $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); - $transferSet = $collector->getExtractedJournals(); - - // filer all of them: - $earnedSet = $this->filterJournalsByTag($earnedSet, $tag); - $spentSet = $this->filterJournalsByTag($spentSet, $tag); - $transferSet = $this->filterJournalsByTag($transferSet, $tag); - - foreach ($dates as $currentDate) { - $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); - $earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']); - $transferred = $this->filterJournalsByDate($transferSet, $currentDate['start'], $currentDate['end']); - $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); - $entries[] - = [ - 'transactions' => 0, - 'title' => $title, - 'route' => route( - 'tags.show', - [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] - ), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + $entries[] = $this->getSingleModelPeriod($tag, $currentDate['period'], $currentDate['start'], $currentDate['end']); } return $entries; @@ -664,12 +441,12 @@ trait PeriodOverview */ protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); - $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); + $range = Navigation::getViewRange(true); + $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; // properties for cache - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('transactions-period-entries'); @@ -679,16 +456,16 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - $spent = []; - $earned = []; - $transferred = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; + $spent = []; + $earned = []; + $transferred = []; // collect all journals in this period (regardless of type) - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTypes($types)->setRange($start, $end); - $genericSet = $collector->getExtractedJournals(); - $loops = 0; + $genericSet = $collector->getExtractedJournals(); + $loops = 0; foreach ($dates as $currentDate) { $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); @@ -706,14 +483,14 @@ trait PeriodOverview } } $entries[] - = [ - 'title' => $title, - 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + = [ + 'title' => $title, + 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; ++$loops; } @@ -750,26 +527,6 @@ trait PeriodOverview return $result; } - private function filterJournalsByTag(array $set, Tag $tag): array - { - $return = []; - foreach ($set as $entry) { - $found = false; - - /** @var array $localTag */ - foreach ($entry['tags'] as $localTag) { - if ($localTag['id'] === $tag->id) { - $found = true; - } - } - if (false === $found) { - continue; - } - $return[] = $entry; - } - - return $return; - } private function filterTransactionsByType(TransactionTypeEnum $type, Carbon $start, Carbon $end): array { @@ -795,39 +552,6 @@ trait PeriodOverview return $result; } - /** - * Return only transactions where $account is the source. - */ - private function filterTransferredAway(Account $account, array $journals): array - { - $return = []; - - /** @var array $journal */ - foreach ($journals as $journal) { - if ($account->id === (int)$journal['source_account_id']) { - $return[] = $journal; - } - } - - return $return; - } - - /** - * Return only transactions where $account is the source. - */ - private function filterTransferredIn(Account $account, array $journals): array - { - $return = []; - - /** @var array $journal */ - foreach ($journals as $journal) { - if ($account->id === (int)$journal['destination_account_id']) { - $return[] = $journal; - } - } - - return $return; - } private function filterTransfers(string $direction, Carbon $start, Carbon $end): array { @@ -863,13 +587,13 @@ trait PeriodOverview /** @var array $journal */ foreach ($journals as $journal) { - $currencyId = (int)$journal['currency_id']; - $currencyCode = $journal['currency_code']; - $currencyName = $journal['currency_name']; - $currencySymbol = $journal['currency_symbol']; - $currencyDecimalPlaces = $journal['currency_decimal_places']; - $foreignCurrencyId = $journal['foreign_currency_id']; - $amount = $journal['amount'] ?? '0'; + $currencyId = (int)$journal['currency_id']; + $currencyCode = $journal['currency_code']; + $currencyName = $journal['currency_name']; + $currencySymbol = $journal['currency_symbol']; + $currencyDecimalPlaces = $journal['currency_decimal_places']; + $foreignCurrencyId = $journal['foreign_currency_id']; + $amount = $journal['amount'] ?? '0'; if ($this->convertToPrimary && $currencyId !== $this->primaryCurrency->id && $foreignCurrencyId !== $this->primaryCurrency->id) { $amount = $journal['pc_amount'] ?? '0'; @@ -898,7 +622,7 @@ trait PeriodOverview ]; - $return[$currencyId]['amount'] = bcadd((string) $return[$currencyId]['amount'], $amount); + $return[$currencyId]['amount'] = bcadd((string)$return[$currencyId]['amount'], $amount); ++$return[$currencyId]['count']; ++$return['count']; } From 1ff47441cefc131e8dec121bbce5eb635884081f Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 27 Sep 2025 16:07:03 +0200 Subject: [PATCH 58/91] Also add no budget and no category overview. --- .../Controllers/Budget/ShowController.php | 2 +- .../Category/NoCategoryController.php | 21 +- app/Models/PeriodStatistic.php | 6 + app/Models/UserGroup.php | 8 + app/Providers/FireflyServiceProvider.php | 13 +- .../PeriodStatisticRepository.php | 84 ++++-- .../PeriodStatisticRepositoryInterface.php | 2 + .../Http/Controllers/PeriodOverview.php | 246 ++++++++++-------- ..._09_25_175248_create_period_statistics.php | 5 + 9 files changed, 245 insertions(+), 142 deletions(-) diff --git a/app/Http/Controllers/Budget/ShowController.php b/app/Http/Controllers/Budget/ShowController.php index 36815436a4..ce344f13e8 100644 --- a/app/Http/Controllers/Budget/ShowController.php +++ b/app/Http/Controllers/Budget/ShowController.php @@ -92,7 +92,7 @@ class ShowController extends Controller // get first journal ever to set off the budget period overview. $first = $this->journalRepos->firstNull(); $firstDate = $first instanceof TransactionJournal ? $first->date : $start; - $periods = $this->getNoBudgetPeriodOverview($firstDate, $end); + $periods = $this->getNoModelPeriodOverview('budget', $firstDate, $end); $page = (int) $request->get('page'); $pageSize = (int) app('preferences')->get('listPageSize', 50)->data; diff --git a/app/Http/Controllers/Category/NoCategoryController.php b/app/Http/Controllers/Category/NoCategoryController.php index be784e6aa6..4c12faeef3 100644 --- a/app/Http/Controllers/Category/NoCategoryController.php +++ b/app/Http/Controllers/Category/NoCategoryController.php @@ -35,6 +35,7 @@ use FireflyIII\Support\Http\Controllers\PeriodOverview; use Illuminate\Contracts\View\Factory; use Illuminate\Http\Request; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Log; use Illuminate\View\View; /** @@ -74,7 +75,7 @@ class NoCategoryController extends Controller */ public function show(Request $request, ?Carbon $start = null, ?Carbon $end = null) { - app('log')->debug('Start of noCategory()'); + Log::debug('Start of noCategory()'); $start ??= session('start'); $end ??= session('end'); @@ -82,14 +83,12 @@ class NoCategoryController extends Controller /** @var Carbon $end */ $page = (int) $request->get('page'); $pageSize = (int) app('preferences')->get('listPageSize', 50)->data; - $subTitle = trans( - 'firefly.without_category_between', - ['start' => $start->isoFormat($this->monthAndDayFormat), 'end' => $end->isoFormat($this->monthAndDayFormat)] - ); - $periods = $this->getNoCategoryPeriodOverview($start); + $subTitle = trans('firefly.without_category_between', ['start' => $start->isoFormat($this->monthAndDayFormat), 'end' => $end->isoFormat($this->monthAndDayFormat)]); + $first = $this->journalRepos->firstNull()->date ?? clone $start; + $periods = $this->getNoModelPeriodOverview('category', $first, $end); - app('log')->debug(sprintf('Start for noCategory() is %s', $start->format('Y-m-d'))); - app('log')->debug(sprintf('End for noCategory() is %s', $end->format('Y-m-d'))); + Log::debug(sprintf('Start for noCategory() is %s', $start->format('Y-m-d'))); + Log::debug(sprintf('End for noCategory() is %s', $end->format('Y-m-d'))); /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); @@ -117,13 +116,13 @@ class NoCategoryController extends Controller $periods = new Collection(); $page = (int) $request->get('page'); $pageSize = (int) app('preferences')->get('listPageSize', 50)->data; - app('log')->debug('Start of noCategory()'); + Log::debug('Start of noCategory()'); $subTitle = (string) trans('firefly.all_journals_without_category'); $first = $this->journalRepos->firstNull(); $start = $first instanceof TransactionJournal ? $first->date : new Carbon(); $end = today(config('app.timezone')); - app('log')->debug(sprintf('Start for noCategory() is %s', $start->format('Y-m-d'))); - app('log')->debug(sprintf('End for noCategory() is %s', $end->format('Y-m-d'))); + Log::debug(sprintf('Start for noCategory() is %s', $start->format('Y-m-d'))); + Log::debug(sprintf('End for noCategory() is %s', $end->format('Y-m-d'))); /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); diff --git a/app/Models/PeriodStatistic.php b/app/Models/PeriodStatistic.php index 9a167fafe0..c8878cfc9b 100644 --- a/app/Models/PeriodStatistic.php +++ b/app/Models/PeriodStatistic.php @@ -8,6 +8,7 @@ use FireflyIII\Casts\SeparateTimezoneCaster; use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphTo; class PeriodStatistic extends Model @@ -24,6 +25,11 @@ class PeriodStatistic extends Model ]; } + public function userGroup(): BelongsTo + { + return $this->belongsTo(UserGroup::class); + } + protected function count(): Attribute { return Attribute::make( diff --git a/app/Models/UserGroup.php b/app/Models/UserGroup.php index 0c142b69cb..e0ff63268a 100644 --- a/app/Models/UserGroup.php +++ b/app/Models/UserGroup.php @@ -76,6 +76,14 @@ class UserGroup extends Model return $this->hasMany(Account::class); } + /** + * Link to accounts. + */ + public function periodStatistics(): HasMany + { + return $this->hasMany(PeriodStatistic::class); + } + /** * Link to attachments. */ diff --git a/app/Providers/FireflyServiceProvider.php b/app/Providers/FireflyServiceProvider.php index cff9567090..e900b72f9e 100644 --- a/app/Providers/FireflyServiceProvider.php +++ b/app/Providers/FireflyServiceProvider.php @@ -163,7 +163,6 @@ class FireflyServiceProvider extends ServiceProvider $this->app->bind(AttachmentHelperInterface::class, AttachmentHelper::class); $this->app->bind(ALERepositoryInterface::class, ALERepository::class); - $this->app->bind(PeriodStatisticRepositoryInterface::class, PeriodStatisticRepository::class); $this->app->bind( static function (Application $app): ObjectGroupRepositoryInterface { @@ -177,6 +176,18 @@ class FireflyServiceProvider extends ServiceProvider } ); + $this->app->bind( + static function (Application $app): PeriodStatisticRepositoryInterface { + /** @var PeriodStatisticRepository $repository */ + $repository = app(PeriodStatisticRepository::class); + if ($app->auth->check()) { // @phpstan-ignore-line (phpstan does not understand the reference to auth) + $repository->setUser(auth()->user()); + } + + return $repository; + } + ); + $this->app->bind( static function (Application $app): WebhookRepositoryInterface { /** @var WebhookRepository $repository */ diff --git a/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php index 07523c5348..3606ddf0d8 100644 --- a/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php +++ b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php @@ -25,37 +25,40 @@ namespace FireflyIII\Repositories\PeriodStatistic; use Carbon\Carbon; use FireflyIII\Models\PeriodStatistic; +use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface; +use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; -class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface +class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, UserGroupInterface { + use UserGroupTrait; + public function findPeriodStatistics(Model $model, Carbon $start, Carbon $end, array $types): Collection { return $model->primaryPeriodStatistics() - ->where('start', $start) - ->where('end', $end) - ->whereIn('type', $types) - ->get() - ; + ->where('start', $start) + ->where('end', $end) + ->whereIn('type', $types) + ->get(); } public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection { return $model->primaryPeriodStatistics() - ->where('start', $start) - ->where('end', $end) - ->where('type', $type) - ->get() - ; + ->where('start', $start) + ->where('end', $end) + ->where('type', $type) + ->get(); } public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic { - $stat = new PeriodStatistic(); + $stat = new PeriodStatistic(); $stat->primaryStatable()->associate($model); $stat->transaction_currency_id = $currencyId; + $stat->user_group_id = $this->getUserGroup()->id; $stat->start = $start; $stat->start_tz = $start->format('e'); $stat->end = $end; @@ -66,16 +69,16 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface $stat->save(); Log::debug(sprintf( - 'Saved #%d [currency #%d, Model %s #%d, %s to %s, %d, %s] as new statistic.', - $stat->id, - $model::class, - $model->id, - $stat->transaction_currency_id, - $stat->start->toW3cString(), - $stat->end->toW3cString(), - $count, - $amount - )); + 'Saved #%d [currency #%d, Model %s #%d, %s to %s, %d, %s] as new statistic.', + $stat->id, + $model::class, + $model->id, + $stat->transaction_currency_id, + $stat->start->toW3cString(), + $stat->end->toW3cString(), + $count, + $amount + )); return $stat; } @@ -89,4 +92,41 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface { $model->primaryPeriodStatistics()->where('start', '<=', $date)->where('end', '>=', $date)->delete(); } + + #[\Override] + public function allInRangeForPrefix(string $prefix, Carbon $start, Carbon $end): Collection + { + return $this->userGroup->periodStatistics() + ->where('type', 'LIKE', sprintf('%s%%', $prefix)) + ->where('start', '>=', $start)->where('end', '<=', $end)->get(); + } + + #[\Override] + public function savePrefixedStatistic(string $prefix, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic + { + $stat = new PeriodStatistic(); + $stat->transaction_currency_id = $currencyId; + $stat->user_group_id = $this->getUserGroup()->id; + $stat->start = $start; + $stat->start_tz = $start->format('e'); + $stat->end = $end; + $stat->end_tz = $end->format('e'); + $stat->amount = $amount; + $stat->count = $count; + $stat->type = sprintf('%s_%s',$prefix, $type); + $stat->save(); + + Log::debug(sprintf( + 'Saved #%d [currency #%d, type "%s", %s to %s, %d, %s] as new statistic.', + $stat->id, + $stat->transaction_currency_id, + $stat->type, + $stat->start->toW3cString(), + $stat->end->toW3cString(), + $count, + $amount + )); + + return $stat; + } } diff --git a/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php b/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php index 6e4f7cf422..fb464e9794 100644 --- a/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php +++ b/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php @@ -35,8 +35,10 @@ interface PeriodStatisticRepositoryInterface public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection; public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic; + public function savePrefixedStatistic(string $prefix, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic; public function allInRangeForModel(Model $model, Carbon $start, Carbon $end): Collection; + public function allInRangeForPrefix(string $prefix, Carbon $start, Carbon $end): Collection; public function deleteStatisticsForModel(Model $model, Carbon $date): void; } diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index e5b020e772..edb51c7ee3 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -169,117 +169,129 @@ trait PeriodOverview * * @throws FireflyException */ - protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array + protected function getNoModelPeriodOverview(string $model, Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); - + Log::debug(sprintf('Now in getNoModelPeriodOverview(%s, %s %s)', $model, $start->format('Y-m-d'), $end->format('Y-m-d'))); + $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); + $range = Navigation::getViewRange(true); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; - $cache = new CacheProperties(); - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty($this->convertToPrimary); - $cache->addProperty('no-budget-period-entries'); - - if ($cache->has()) { - return $cache->get(); - } - /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - - // get all expenses without a budget. - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->setRange($start, $end)->withoutBudget()->withAccountInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $journals = $collector->getExtractedJournals(); + $dates = Navigation::blockPeriods($start, $end, $range); + [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); + $entries = []; + $this->statistics = $this->periodStatisticRepo->allInRangeForPrefix(sprintf('no_%s', $model), $start, $end); + Log::debug(sprintf('Collected %d stats', $this->statistics->count())); foreach ($dates as $currentDate) { - $set = $this->filterJournalsByDate($journals, $currentDate['start'], $currentDate['end']); - $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); - $entries[] - = [ - 'title' => $title, - 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($set), - 'spent' => $this->groupByCurrency($set), - 'earned' => [], - 'transferred_away' => [], - 'transferred_in' => [], - ]; + $entries[] = $this->getSingleNoModelPeriodOverview($model, $currentDate['start'], $currentDate['end'], $currentDate['period']); } - $cache->store($entries); return $entries; } - /** - * TODO fix the date. - * - * Show period overview for no category view. - * - * @throws FireflyException - */ - protected function getNoCategoryPeriodOverview(Carbon $theDate): array + private function getSingleNoModelPeriodOverview(string $model, Carbon $start, Carbon $end, string $period): array { - Log::debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d'))); - $range = Navigation::getViewRange(true); - $first = $this->journalRepos->firstNull(); - $start = null === $first ? new Carbon() : $first->date; - $end = clone $theDate; - $end = Navigation::endOfPeriod($end, $range); + Log::debug(sprintf('getSingleNoModelPeriodOverview(%s, %s, %s, %s)', $model, $start->format('Y-m-d'), $end->format('Y-m-d'), $period)); + $statistics = $this->filterPrefixedStatistics($start, $end, sprintf('no_%s', $model)); + $title = Navigation::periodShow($end, $period); - Log::debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d'))); - Log::debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d'))); + if (0 === $statistics->count()) { + Log::debug(sprintf('Found no statistics in period %s - %s, regenerating them.', $start->format('Y-m-d'), $end->format('Y-m-d'))); + switch ($model) { + default: + throw new FireflyException(sprintf('Cannot deal with model of type "%s"', $model)); + case 'budget': + // get all expenses without a budget. + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setRange($start, $end)->withoutBudget()->withAccountInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); + $spent = $collector->getExtractedJournals(); + $earned = []; + $transferred = []; + break; + case 'category': + // collect all expenses in this period: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->withoutCategory(); + $collector->setRange($start, $end); + $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); + $earned = $collector->getExtractedJournals(); - // properties for cache - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; + // collect all income in this period: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->withoutCategory(); + $collector->setRange($start, $end); + $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); + $spent = $collector->getExtractedJournals(); - // collect all expenses in this period: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->withoutCategory(); - $collector->setRange($start, $end); - $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earnedSet = $collector->getExtractedJournals(); - - // collect all income in this period: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->withoutCategory(); - $collector->setRange($start, $end); - $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spentSet = $collector->getExtractedJournals(); - - // collect all transfers in this period: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->withoutCategory(); - $collector->setRange($start, $end); - $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); - $transferSet = $collector->getExtractedJournals(); - - /** @var array $currentDate */ - foreach ($dates as $currentDate) { - $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); - $earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']); - $transferred = $this->filterJournalsByDate($transferSet, $currentDate['start'], $currentDate['end']); - $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); - $entries[] - = [ + // collect all transfers in this period: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->withoutCategory(); + $collector->setRange($start, $end); + $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); + $transferred = $collector->getExtractedJournals(); + break; + } + $groupedSpent = $this->groupByCurrency($spent); + $groupedEarned = $this->groupByCurrency($earned); + $groupedTransferred = $this->groupByCurrency($transferred); + $entry + = [ 'title' => $title, - 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), + 'route' => route(sprintf('%s.no-%s', Str::plural($model), $model), [$start->format('Y-m-d'), $end->format('Y-m-d')]), + 'total_transactions' => count($spent), + 'spent' => $groupedSpent, + 'earned' => $groupedEarned, + 'transferred' => $groupedTransferred, ]; + $this->saveGroupedForPrefix(sprintf('no_%s', $model), $start, $end, 'spent', $groupedSpent); + $this->saveGroupedForPrefix(sprintf('no_%s', $model), $start, $end, 'earned', $groupedEarned); + $this->saveGroupedForPrefix(sprintf('no_%s', $model), $start, $end, 'transferred', $groupedTransferred); + return $entry; } - Log::debug('End of loops'); + Log::debug(sprintf('Found %d statistics in period %s - %s.', count($statistics), $start->format('Y-m-d'), $end->format('Y-m-d'))); - return $entries; + $entry + = [ + 'title' => $title, + 'route' => route(sprintf('%s.no-%s', Str::plural($model), $model), [$start->format('Y-m-d'), $end->format('Y-m-d')]), + 'total_transactions' => 0, + 'spent' => [], + 'earned' => [], + 'transferred' => [], + ]; + $grouped = []; + /** @var PeriodStatistic $statistic */ + foreach ($statistics as $statistic) { + $type = str_replace(sprintf('no_%s_', $model), '', $statistic->type); + $id = (int)$statistic->transaction_currency_id; + $currency = Amount::getTransactionCurrencyById($id); + $grouped[$type]['count'] ??= 0; + $grouped[$type][$id] = [ + 'amount' => (string)$statistic->amount, + 'count' => (int)$statistic->count, + 'currency_id' => $currency->id, + 'currency_name' => $currency->name, + 'currency_code' => $currency->code, + 'currency_symbol' => $currency->symbol, + 'currency_decimal_places' => $currency->decimal_places, + ]; + $grouped[$type]['count'] += (int)$statistic->count; + } + $types = ['spent', 'earned', 'transferred']; + foreach ($types as $type) { + if (array_key_exists($type, $grouped)) { + $entry['total_transactions'] += $grouped[$type]['count']; + unset($grouped[$type]['count']); + $entry[$type] = $grouped[$type]; + } + + } + return $entry; } protected function getSingleModelPeriod(Model $model, string $period, Carbon $start, Carbon $end): array @@ -303,30 +315,34 @@ trait PeriodOverview } - protected function filterStatistics(Carbon $start, Carbon $end, string $type): Collection + private function filterStatistics(Carbon $start, Carbon $end, string $type): Collection { + if (0 === $this->statistics->count()) { + Log::warning('Have no statistic to filter!'); + return new Collection; + } return $this->statistics->filter( function (PeriodStatistic $statistic) use ($start, $end, $type) { - if ( - !$statistic->end->equalTo($end) - && $statistic->end->format('Y-m-d H:i:s') === $end->format('Y-m-d H:i:s') - ) { - echo sprintf('End: "%s" vs "%s": %s', $statistic->end->toW3cString(), $end->toW3cString(), var_export($statistic->end->eq($end), true)); - var_dump($statistic->end); - var_dump($end); - - exit; - } - - return $statistic->start->eq($start) && $statistic->end->eq($end) && $statistic->type === $type; } ); } + private function filterPrefixedStatistics(Carbon $start, Carbon $end, string $prefix): Collection + { + if (0 === $this->statistics->count()) { + Log::warning('Have no statistic to filter!'); + return new Collection; + } + return $this->statistics->filter( + function (PeriodStatistic $statistic) use ($start, $end, $prefix) { + return $statistic->start->eq($start) && $statistic->end->eq($end) && str_starts_with($statistic->type, $prefix); + } + ); + } - protected function getSingleModelPeriodByType(Model $model, Carbon $start, Carbon $end, string $type): array + private function getSingleModelPeriodByType(Model $model, Carbon $start, Carbon $end, string $type): array { Log::debug(sprintf('Now in getSingleModelPeriodByType(%s #%d, %s %s, %s)', $model::class, $model->id, $start->format('Y-m-d'), $end->format('Y-m-d'), $type)); $statistics = $this->filterStatistics($start, $end, $type); @@ -497,7 +513,7 @@ trait PeriodOverview return $entries; } - protected function saveGroupedAsStatistics(Model $model, Carbon $start, Carbon $end, string $type, array $array): void + private function saveGroupedAsStatistics(Model $model, Carbon $start, Carbon $end, string $type, array $array): void { unset($array['count']); Log::debug(sprintf('saveGroupedAsStatistics(%s #%d, %s, %s, "%s", array(%d))', $model::class, $model->id, $start->format('Y-m-d'), $end->format('Y-m-d'), $type, count($array))); @@ -510,6 +526,19 @@ trait PeriodOverview } } + private function saveGroupedForPrefix(string $prefix, Carbon $start, Carbon $end, string $type, array $array): void + { + unset($array['count']); + Log::debug(sprintf('saveGroupedForPrefix("%s", %s, %s, "%s", array(%d))', $prefix, $start->format('Y-m-d'), $end->format('Y-m-d'), $type, count($array))); + foreach ($array as $entry) { + $this->periodStatisticRepo->savePrefixedStatistic($prefix, $entry['currency_id'], $start, $end, $type, $entry['count'], $entry['amount']); + } + if (0 === count($array)) { + Log::debug('Save empty statistic.'); + $this->periodStatisticRepo->savePrefixedStatistic($prefix, $this->primaryCurrency->id, $start, $end, $type, 0, '0'); + } + } + /** * Filter a list of journals by a set of dates, and then group them by currency. */ @@ -584,6 +613,9 @@ trait PeriodOverview $return = [ 'count' => 0, ]; + if (0 === count($journals)) { + return $return; + } /** @var array $journal */ foreach ($journals as $journal) { diff --git a/database/migrations/2025_09_25_175248_create_period_statistics.php b/database/migrations/2025_09_25_175248_create_period_statistics.php index 0a5bf8d86b..0cda62b0ef 100644 --- a/database/migrations/2025_09_25_175248_create_period_statistics.php +++ b/database/migrations/2025_09_25_175248_create_period_statistics.php @@ -14,6 +14,10 @@ return new class extends Migration Schema::create('period_statistics', function (Blueprint $table) { $table->id(); $table->timestamps(); + + // reference to user group id. + $table->bigInteger('user_group_id', false, true); + $table->integer('primary_statable_id', false, true)->nullable(); $table->string('primary_statable_type', 255)->nullable(); @@ -33,6 +37,7 @@ return new class extends Migration $table->string('type',255); $table->integer('count', false, true)->default(0); $table->decimal('amount', 32, 12); + $table->foreign('user_group_id')->references('id')->on('user_groups')->onDelete('cascade'); }); } From a02c4b42a4bfc78fb6d65798e1a36deed7a98bff Mon Sep 17 00:00:00 2001 From: Maxim Kurbatov Date: Sun, 28 Sep 2025 11:49:37 +0500 Subject: [PATCH 59/91] Add Kazakhstani Tenge (KZT) currency Signed-off-by: Maxim Kurbatov --- database/seeders/TransactionCurrencySeeder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/database/seeders/TransactionCurrencySeeder.php b/database/seeders/TransactionCurrencySeeder.php index e29de2d63c..ce75ec253d 100644 --- a/database/seeders/TransactionCurrencySeeder.php +++ b/database/seeders/TransactionCurrencySeeder.php @@ -80,6 +80,7 @@ class TransactionCurrencySeeder extends Seeder $currencies[] = ['code' => 'CHF', 'name' => 'Swiss franc', 'symbol' => 'CHF', 'decimal_places' => 2]; $currencies[] = ['code' => 'NOK', 'name' => 'Norwegian krone', 'symbol' => 'kr.', 'decimal_places' => 2]; $currencies[] = ['code' => 'CZK', 'name' => 'Czech koruna', 'symbol' => 'Kč', 'decimal_places' => 2]; + $currencies[] = ['code' => 'KZT', 'name' => 'Kazakhstani tenge', 'symbol' => '₸', 'decimal_places' => 2]; foreach ($currencies as $currency) { if (null === TransactionCurrency::where('code', $currency['code'])->first()) { From 48255ae6eea79b8e16d66d43b1d305d484068aeb Mon Sep 17 00:00:00 2001 From: JC5 Date: Mon, 29 Sep 2025 05:22:25 +0200 Subject: [PATCH 60/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-09-29?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .ci/php-cs-fixer/composer.lock | 37 ++-- .../Category/NoCategoryController.php | 2 +- .../PeriodStatisticRepository.php | 72 ++++--- .../PeriodStatisticRepositoryInterface.php | 2 + .../Http/Controllers/PeriodOverview.php | 193 +++++++++-------- composer.lock | 183 ++++++++-------- config/firefly.php | 4 +- package-lock.json | 200 +++++++++--------- resources/assets/v1/src/locales/de.json | 2 +- 9 files changed, 356 insertions(+), 339 deletions(-) diff --git a/.ci/php-cs-fixer/composer.lock b/.ci/php-cs-fixer/composer.lock index f397529f05..4e1d937036 100644 --- a/.ci/php-cs-fixer/composer.lock +++ b/.ci/php-cs-fixer/composer.lock @@ -1252,16 +1252,16 @@ }, { "name": "symfony/console", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7" + "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", - "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", + "url": "https://api.github.com/repos/symfony/console/zipball/2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", + "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", "shasum": "" }, "require": { @@ -1326,7 +1326,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.3" + "source": "https://github.com/symfony/console/tree/v7.3.4" }, "funding": [ { @@ -1346,7 +1346,7 @@ "type": "tidelift" } ], - "time": "2025-08-25T06:35:40+00:00" + "time": "2025-09-22T15:31:00+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2365,16 +2365,16 @@ }, { "name": "symfony/process", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "32241012d521e2e8a9d713adb0812bb773b907f1" + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1", - "reference": "32241012d521e2e8a9d713adb0812bb773b907f1", + "url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b", + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b", "shasum": "" }, "require": { @@ -2406,7 +2406,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.3" + "source": "https://github.com/symfony/process/tree/v7.3.4" }, "funding": [ { @@ -2426,7 +2426,7 @@ "type": "tidelift" } ], - "time": "2025-08-18T09:42:54+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/service-contracts", @@ -2575,16 +2575,16 @@ }, { "name": "symfony/string", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c" + "reference": "f96476035142921000338bad71e5247fbc138872" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", - "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", + "url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872", + "reference": "f96476035142921000338bad71e5247fbc138872", "shasum": "" }, "require": { @@ -2599,7 +2599,6 @@ }, "require-dev": { "symfony/emoji": "^7.1", - "symfony/error-handler": "^6.4|^7.0", "symfony/http-client": "^6.4|^7.0", "symfony/intl": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3.0", @@ -2642,7 +2641,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.3" + "source": "https://github.com/symfony/string/tree/v7.3.4" }, "funding": [ { @@ -2662,7 +2661,7 @@ "type": "tidelift" } ], - "time": "2025-08-25T06:35:40+00:00" + "time": "2025-09-11T14:36:48+00:00" } ], "packages-dev": [], diff --git a/app/Http/Controllers/Category/NoCategoryController.php b/app/Http/Controllers/Category/NoCategoryController.php index 4c12faeef3..11f6ade3a9 100644 --- a/app/Http/Controllers/Category/NoCategoryController.php +++ b/app/Http/Controllers/Category/NoCategoryController.php @@ -84,7 +84,7 @@ class NoCategoryController extends Controller $page = (int) $request->get('page'); $pageSize = (int) app('preferences')->get('listPageSize', 50)->data; $subTitle = trans('firefly.without_category_between', ['start' => $start->isoFormat($this->monthAndDayFormat), 'end' => $end->isoFormat($this->monthAndDayFormat)]); - $first = $this->journalRepos->firstNull()->date ?? clone $start; + $first = $this->journalRepos->firstNull()->date ?? clone $start; $periods = $this->getNoModelPeriodOverview('category', $first, $end); Log::debug(sprintf('Start for noCategory() is %s', $start->format('Y-m-d'))); diff --git a/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php index 3606ddf0d8..ad294b9fcb 100644 --- a/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php +++ b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php @@ -30,6 +30,7 @@ use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; +use Override; class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, UserGroupInterface { @@ -38,24 +39,26 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U public function findPeriodStatistics(Model $model, Carbon $start, Carbon $end, array $types): Collection { return $model->primaryPeriodStatistics() - ->where('start', $start) - ->where('end', $end) - ->whereIn('type', $types) - ->get(); + ->where('start', $start) + ->where('end', $end) + ->whereIn('type', $types) + ->get() + ; } public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection { return $model->primaryPeriodStatistics() - ->where('start', $start) - ->where('end', $end) - ->where('type', $type) - ->get(); + ->where('start', $start) + ->where('end', $end) + ->where('type', $type) + ->get() + ; } public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic { - $stat = new PeriodStatistic(); + $stat = new PeriodStatistic(); $stat->primaryStatable()->associate($model); $stat->transaction_currency_id = $currencyId; $stat->user_group_id = $this->getUserGroup()->id; @@ -69,16 +72,16 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U $stat->save(); Log::debug(sprintf( - 'Saved #%d [currency #%d, Model %s #%d, %s to %s, %d, %s] as new statistic.', - $stat->id, - $model::class, - $model->id, - $stat->transaction_currency_id, - $stat->start->toW3cString(), - $stat->end->toW3cString(), - $count, - $amount - )); + 'Saved #%d [currency #%d, Model %s #%d, %s to %s, %d, %s] as new statistic.', + $stat->id, + $model::class, + $model->id, + $stat->transaction_currency_id, + $stat->start->toW3cString(), + $stat->end->toW3cString(), + $count, + $amount + )); return $stat; } @@ -93,18 +96,19 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U $model->primaryPeriodStatistics()->where('start', '<=', $date)->where('end', '>=', $date)->delete(); } - #[\Override] + #[Override] public function allInRangeForPrefix(string $prefix, Carbon $start, Carbon $end): Collection { return $this->userGroup->periodStatistics() - ->where('type', 'LIKE', sprintf('%s%%', $prefix)) - ->where('start', '>=', $start)->where('end', '<=', $end)->get(); + ->where('type', 'LIKE', sprintf('%s%%', $prefix)) + ->where('start', '>=', $start)->where('end', '<=', $end)->get() + ; } - #[\Override] + #[Override] public function savePrefixedStatistic(string $prefix, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic { - $stat = new PeriodStatistic(); + $stat = new PeriodStatistic(); $stat->transaction_currency_id = $currencyId; $stat->user_group_id = $this->getUserGroup()->id; $stat->start = $start; @@ -113,19 +117,19 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U $stat->end_tz = $end->format('e'); $stat->amount = $amount; $stat->count = $count; - $stat->type = sprintf('%s_%s',$prefix, $type); + $stat->type = sprintf('%s_%s', $prefix, $type); $stat->save(); Log::debug(sprintf( - 'Saved #%d [currency #%d, type "%s", %s to %s, %d, %s] as new statistic.', - $stat->id, - $stat->transaction_currency_id, - $stat->type, - $stat->start->toW3cString(), - $stat->end->toW3cString(), - $count, - $amount - )); + 'Saved #%d [currency #%d, type "%s", %s to %s, %d, %s] as new statistic.', + $stat->id, + $stat->transaction_currency_id, + $stat->type, + $stat->start->toW3cString(), + $stat->end->toW3cString(), + $count, + $amount + )); return $stat; } diff --git a/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php b/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php index fb464e9794..22f17d8f2d 100644 --- a/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php +++ b/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php @@ -35,9 +35,11 @@ interface PeriodStatisticRepositoryInterface public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection; public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic; + public function savePrefixedStatistic(string $prefix, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic; public function allInRangeForModel(Model $model, Carbon $start, Carbon $end): Collection; + public function allInRangeForPrefix(string $prefix, Carbon $start, Carbon $end): Collection; public function deleteStatisticsForModel(Model $model, Carbon $date): void; diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index edb51c7ee3..5209eab2fe 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -93,18 +93,18 @@ trait PeriodOverview protected function getAccountPeriodOverview(Account $account, Carbon $start, Carbon $end): array { Log::debug(sprintf('Now in getAccountPeriodOverview(#%d, %s %s)', $account->id, $start->format('Y-m-d H:i:s.u'), $end->format('Y-m-d H:i:s.u'))); - $this->accountRepository = app(AccountRepositoryInterface::class); + $this->accountRepository = app(AccountRepositoryInterface::class); $this->accountRepository->setUser($account->user); $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); $range = Navigation::getViewRange(true); - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); - $this->statistics = $this->periodStatisticRepo->allInRangeForModel($account, $start, $end); + $dates = Navigation::blockPeriods($start, $end, $range); + [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); + $this->statistics = $this->periodStatisticRepo->allInRangeForModel($account, $start, $end); - $entries = []; + $entries = []; Log::debug(sprintf('Count of loops: %d', count($dates))); foreach ($dates as $currentDate) { $entries[] = $this->getSingleModelPeriod($account, $currentDate['period'], $currentDate['start'], $currentDate['end']); @@ -140,18 +140,18 @@ trait PeriodOverview */ protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array { - $this->categoryRepository = app(CategoryRepositoryInterface::class); + $this->categoryRepository = app(CategoryRepositoryInterface::class); $this->categoryRepository->setUser($category->user); $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); - $range = Navigation::getViewRange(true); - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + $range = Navigation::getViewRange(true); + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); - $this->statistics = $this->periodStatisticRepo->allInRangeForModel($category, $start, $end); + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; + [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); + $this->statistics = $this->periodStatisticRepo->allInRangeForModel($category, $start, $end); Log::debug(sprintf('Count of loops: %d', count($dates))); @@ -174,13 +174,13 @@ trait PeriodOverview Log::debug(sprintf('Now in getNoModelPeriodOverview(%s, %s %s)', $model, $start->format('Y-m-d'), $end->format('Y-m-d'))); $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); $range = Navigation::getViewRange(true); - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); - $entries = []; - $this->statistics = $this->periodStatisticRepo->allInRangeForPrefix(sprintf('no_%s', $model), $start, $end); + $dates = Navigation::blockPeriods($start, $end, $range); + [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); + $entries = []; + $this->statistics = $this->periodStatisticRepo->allInRangeForPrefix(sprintf('no_%s', $model), $start, $end); Log::debug(sprintf('Collected %d stats', $this->statistics->count())); foreach ($dates as $currentDate) { @@ -198,42 +198,47 @@ trait PeriodOverview if (0 === $statistics->count()) { Log::debug(sprintf('Found no statistics in period %s - %s, regenerating them.', $start->format('Y-m-d'), $end->format('Y-m-d'))); + switch ($model) { default: throw new FireflyException(sprintf('Cannot deal with model of type "%s"', $model)); + case 'budget': // get all expenses without a budget. /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end)->withoutBudget()->withAccountInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); $spent = $collector->getExtractedJournals(); $earned = []; $transferred = []; + break; + case 'category': // collect all expenses in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); - $earned = $collector->getExtractedJournals(); + $earned = $collector->getExtractedJournals(); // collect all income in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $spent = $collector->getExtractedJournals(); + $spent = $collector->getExtractedJournals(); // collect all transfers in this period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->withoutCategory(); $collector->setRange($start, $end); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]); $transferred = $collector->getExtractedJournals(); + break; } $groupedSpent = $this->groupByCurrency($spent); @@ -241,37 +246,39 @@ trait PeriodOverview $groupedTransferred = $this->groupByCurrency($transferred); $entry = [ - 'title' => $title, - 'route' => route(sprintf('%s.no-%s', Str::plural($model), $model), [$start->format('Y-m-d'), $end->format('Y-m-d')]), - 'total_transactions' => count($spent), - 'spent' => $groupedSpent, - 'earned' => $groupedEarned, - 'transferred' => $groupedTransferred, - ]; + 'title' => $title, + 'route' => route(sprintf('%s.no-%s', Str::plural($model), $model), [$start->format('Y-m-d'), $end->format('Y-m-d')]), + 'total_transactions' => count($spent), + 'spent' => $groupedSpent, + 'earned' => $groupedEarned, + 'transferred' => $groupedTransferred, + ]; $this->saveGroupedForPrefix(sprintf('no_%s', $model), $start, $end, 'spent', $groupedSpent); $this->saveGroupedForPrefix(sprintf('no_%s', $model), $start, $end, 'earned', $groupedEarned); $this->saveGroupedForPrefix(sprintf('no_%s', $model), $start, $end, 'transferred', $groupedTransferred); + return $entry; } Log::debug(sprintf('Found %d statistics in period %s - %s.', count($statistics), $start->format('Y-m-d'), $end->format('Y-m-d'))); $entry - = [ - 'title' => $title, - 'route' => route(sprintf('%s.no-%s', Str::plural($model), $model), [$start->format('Y-m-d'), $end->format('Y-m-d')]), - 'total_transactions' => 0, - 'spent' => [], - 'earned' => [], - 'transferred' => [], - ]; - $grouped = []; + = [ + 'title' => $title, + 'route' => route(sprintf('%s.no-%s', Str::plural($model), $model), [$start->format('Y-m-d'), $end->format('Y-m-d')]), + 'total_transactions' => 0, + 'spent' => [], + 'earned' => [], + 'transferred' => [], + ]; + $grouped = []; + /** @var PeriodStatistic $statistic */ foreach ($statistics as $statistic) { - $type = str_replace(sprintf('no_%s_', $model), '', $statistic->type); - $id = (int)$statistic->transaction_currency_id; - $currency = Amount::getTransactionCurrencyById($id); + $type = str_replace(sprintf('no_%s_', $model), '', $statistic->type); + $id = (int)$statistic->transaction_currency_id; + $currency = Amount::getTransactionCurrencyById($id); $grouped[$type]['count'] ??= 0; - $grouped[$type][$id] = [ + $grouped[$type][$id] = [ 'amount' => (string)$statistic->amount, 'count' => (int)$statistic->count, 'currency_id' => $currency->id, @@ -282,7 +289,7 @@ trait PeriodOverview ]; $grouped[$type]['count'] += (int)$statistic->count; } - $types = ['spent', 'earned', 'transferred']; + $types = ['spent', 'earned', 'transferred']; foreach ($types as $type) { if (array_key_exists($type, $grouped)) { $entry['total_transactions'] += $grouped[$type]['count']; @@ -291,6 +298,7 @@ trait PeriodOverview } } + return $entry; } @@ -305,7 +313,7 @@ trait PeriodOverview ]; $this->transactions = []; foreach ($types as $type) { - $set = $this->getSingleModelPeriodByType($model, $start, $end, $type); + $set = $this->getSingleModelPeriodByType($model, $start, $end, $type); $return['total_transactions'] += $set['count']; unset($set['count']); $return[$type] = $set; @@ -314,13 +322,14 @@ trait PeriodOverview return $return; } - private function filterStatistics(Carbon $start, Carbon $end, string $type): Collection { if (0 === $this->statistics->count()) { Log::warning('Have no statistic to filter!'); - return new Collection; + + return new Collection(); } + return $this->statistics->filter( function (PeriodStatistic $statistic) use ($start, $end, $type) { return $statistic->start->eq($start) && $statistic->end->eq($end) && $statistic->type === $type; @@ -332,8 +341,10 @@ trait PeriodOverview { if (0 === $this->statistics->count()) { Log::warning('Have no statistic to filter!'); - return new Collection; + + return new Collection(); } + return $this->statistics->filter( function (PeriodStatistic $statistic) use ($start, $end, $prefix) { return $statistic->start->eq($start) && $statistic->end->eq($end) && str_starts_with($statistic->type, $prefix); @@ -341,7 +352,6 @@ trait PeriodOverview ); } - private function getSingleModelPeriodByType(Model $model, Carbon $start, Carbon $end, string $type): array { Log::debug(sprintf('Now in getSingleModelPeriodByType(%s #%d, %s %s, %s)', $model::class, $model->id, $start->format('Y-m-d'), $end->format('Y-m-d'), $type)); @@ -354,14 +364,20 @@ trait PeriodOverview switch ($model::class) { default: throw new FireflyException(sprintf('Cannot deal with model of type "%s"', $model::class)); + case Category::class: $this->transactions = $this->categoryRepository->periodCollection($model, $start, $end); + break; + case Account::class: $this->transactions = $this->accountRepository->periodCollection($model, $start, $end); + break; + case Tag::class: $this->transactions = $this->tagRepository->periodCollection($model, $start, $end); + break; } } @@ -399,15 +415,15 @@ trait PeriodOverview return $grouped; } - $grouped = [ + $grouped = [ 'count' => 0, ]; /** @var PeriodStatistic $statistic */ foreach ($statistics as $statistic) { - $id = (int)$statistic->transaction_currency_id; - $currency = Amount::getTransactionCurrencyById($id); - $grouped[$id] = [ + $id = (int)$statistic->transaction_currency_id; + $currency = Amount::getTransactionCurrencyById($id); + $grouped[$id] = [ 'amount' => (string)$statistic->amount, 'count' => (int)$statistic->count, 'currency_id' => $currency->id, @@ -422,7 +438,6 @@ trait PeriodOverview return $grouped; } - /** * This shows a period overview for a tag. It goes back in time and lists all relevant transactions and sums. * @@ -430,18 +445,18 @@ trait PeriodOverview */ protected function getTagPeriodOverview(Tag $tag, Carbon $start, Carbon $end): array // period overview for tags. { - $this->tagRepository = app(TagRepositoryInterface::class); + $this->tagRepository = app(TagRepositoryInterface::class); $this->tagRepository->setUser($tag->user); $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); - $range = Navigation::getViewRange(true); - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + $range = Navigation::getViewRange(true); + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); - $this->statistics = $this->periodStatisticRepo->allInRangeForModel($tag, $start, $end); + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; + [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); + $this->statistics = $this->periodStatisticRepo->allInRangeForModel($tag, $start, $end); Log::debug(sprintf('Count of loops: %d', count($dates))); @@ -457,12 +472,12 @@ trait PeriodOverview */ protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array { - $range = Navigation::getViewRange(true); - $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); + $range = Navigation::getViewRange(true); + $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; // properties for cache - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('transactions-period-entries'); @@ -472,16 +487,16 @@ trait PeriodOverview } /** @var array $dates */ - $dates = Navigation::blockPeriods($start, $end, $range); - $entries = []; - $spent = []; - $earned = []; - $transferred = []; + $dates = Navigation::blockPeriods($start, $end, $range); + $entries = []; + $spent = []; + $earned = []; + $transferred = []; // collect all journals in this period (regardless of type) - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setTypes($types)->setRange($start, $end); - $genericSet = $collector->getExtractedJournals(); - $loops = 0; + $genericSet = $collector->getExtractedJournals(); + $loops = 0; foreach ($dates as $currentDate) { $title = Navigation::periodShow($currentDate['end'], $currentDate['period']); @@ -499,14 +514,14 @@ trait PeriodOverview } } $entries[] - = [ - 'title' => $title, - 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + = [ + 'title' => $title, + 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; ++$loops; } @@ -556,7 +571,6 @@ trait PeriodOverview return $result; } - private function filterTransactionsByType(TransactionTypeEnum $type, Carbon $start, Carbon $end): array { $result = []; @@ -581,7 +595,6 @@ trait PeriodOverview return $result; } - private function filterTransfers(string $direction, Carbon $start, Carbon $end): array { $result = []; @@ -619,13 +632,13 @@ trait PeriodOverview /** @var array $journal */ foreach ($journals as $journal) { - $currencyId = (int)$journal['currency_id']; - $currencyCode = $journal['currency_code']; - $currencyName = $journal['currency_name']; - $currencySymbol = $journal['currency_symbol']; - $currencyDecimalPlaces = $journal['currency_decimal_places']; - $foreignCurrencyId = $journal['foreign_currency_id']; - $amount = $journal['amount'] ?? '0'; + $currencyId = (int)$journal['currency_id']; + $currencyCode = $journal['currency_code']; + $currencyName = $journal['currency_name']; + $currencySymbol = $journal['currency_symbol']; + $currencyDecimalPlaces = $journal['currency_decimal_places']; + $foreignCurrencyId = $journal['foreign_currency_id']; + $amount = $journal['amount'] ?? '0'; if ($this->convertToPrimary && $currencyId !== $this->primaryCurrency->id && $foreignCurrencyId !== $this->primaryCurrency->id) { $amount = $journal['pc_amount'] ?? '0'; diff --git a/composer.lock b/composer.lock index f3717d8cb4..f7341ff02a 100644 --- a/composer.lock +++ b/composer.lock @@ -6488,16 +6488,16 @@ }, { "name": "symfony/cache", - "version": "v7.3.2", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "6621a2bee5373e3e972b2ae5dbedd5ac899d8cb6" + "reference": "bf8afc8ffd4bfd3d9c373e417f041d9f1e5b863f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/6621a2bee5373e3e972b2ae5dbedd5ac899d8cb6", - "reference": "6621a2bee5373e3e972b2ae5dbedd5ac899d8cb6", + "url": "https://api.github.com/repos/symfony/cache/zipball/bf8afc8ffd4bfd3d9c373e417f041d9f1e5b863f", + "reference": "bf8afc8ffd4bfd3d9c373e417f041d9f1e5b863f", "shasum": "" }, "require": { @@ -6566,7 +6566,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v7.3.2" + "source": "https://github.com/symfony/cache/tree/v7.3.4" }, "funding": [ { @@ -6586,7 +6586,7 @@ "type": "tidelift" } ], - "time": "2025-07-30T17:13:41+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/cache-contracts", @@ -6740,16 +6740,16 @@ }, { "name": "symfony/console", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7" + "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", - "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", + "url": "https://api.github.com/repos/symfony/console/zipball/2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", + "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", "shasum": "" }, "require": { @@ -6814,7 +6814,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.3" + "source": "https://github.com/symfony/console/tree/v7.3.4" }, "funding": [ { @@ -6834,7 +6834,7 @@ "type": "tidelift" } ], - "time": "2025-08-25T06:35:40+00:00" + "time": "2025-09-22T15:31:00+00:00" }, { "name": "symfony/css-selector", @@ -6970,16 +6970,16 @@ }, { "name": "symfony/error-handler", - "version": "v7.3.2", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "0b31a944fcd8759ae294da4d2808cbc53aebd0c3" + "reference": "99f81bc944ab8e5dae4f21b4ca9972698bbad0e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/0b31a944fcd8759ae294da4d2808cbc53aebd0c3", - "reference": "0b31a944fcd8759ae294da4d2808cbc53aebd0c3", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/99f81bc944ab8e5dae4f21b4ca9972698bbad0e4", + "reference": "99f81bc944ab8e5dae4f21b4ca9972698bbad0e4", "shasum": "" }, "require": { @@ -7027,7 +7027,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.3.2" + "source": "https://github.com/symfony/error-handler/tree/v7.3.4" }, "funding": [ { @@ -7047,7 +7047,7 @@ "type": "tidelift" } ], - "time": "2025-07-07T08:17:57+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/event-dispatcher", @@ -7347,16 +7347,16 @@ }, { "name": "symfony/http-client", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019" + "reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019", - "reference": "333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019", + "url": "https://api.github.com/repos/symfony/http-client/zipball/4b62871a01c49457cf2a8e560af7ee8a94b87a62", + "reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62", "shasum": "" }, "require": { @@ -7423,7 +7423,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.3.3" + "source": "https://github.com/symfony/http-client/tree/v7.3.4" }, "funding": [ { @@ -7443,7 +7443,7 @@ "type": "tidelift" } ], - "time": "2025-08-27T07:45:05+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/http-client-contracts", @@ -7525,16 +7525,16 @@ }, { "name": "symfony/http-foundation", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "7475561ec27020196c49bb7c4f178d33d7d3dc00" + "reference": "c061c7c18918b1b64268771aad04b40be41dd2e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7475561ec27020196c49bb7c4f178d33d7d3dc00", - "reference": "7475561ec27020196c49bb7c4f178d33d7d3dc00", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/c061c7c18918b1b64268771aad04b40be41dd2e6", + "reference": "c061c7c18918b1b64268771aad04b40be41dd2e6", "shasum": "" }, "require": { @@ -7584,7 +7584,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.3.3" + "source": "https://github.com/symfony/http-foundation/tree/v7.3.4" }, "funding": [ { @@ -7604,20 +7604,20 @@ "type": "tidelift" } ], - "time": "2025-08-20T08:04:18+00:00" + "time": "2025-09-16T08:38:17+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "72c304de37e1a1cec6d5d12b81187ebd4850a17b" + "reference": "b796dffea7821f035047235e076b60ca2446e3cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/72c304de37e1a1cec6d5d12b81187ebd4850a17b", - "reference": "72c304de37e1a1cec6d5d12b81187ebd4850a17b", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b796dffea7821f035047235e076b60ca2446e3cf", + "reference": "b796dffea7821f035047235e076b60ca2446e3cf", "shasum": "" }, "require": { @@ -7702,7 +7702,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.3.3" + "source": "https://github.com/symfony/http-kernel/tree/v7.3.4" }, "funding": [ { @@ -7722,20 +7722,20 @@ "type": "tidelift" } ], - "time": "2025-08-29T08:23:45+00:00" + "time": "2025-09-27T12:32:17+00:00" }, { "name": "symfony/mailer", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "a32f3f45f1990db8c4341d5122a7d3a381c7e575" + "reference": "ab97ef2f7acf0216955f5845484235113047a31d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/a32f3f45f1990db8c4341d5122a7d3a381c7e575", - "reference": "a32f3f45f1990db8c4341d5122a7d3a381c7e575", + "url": "https://api.github.com/repos/symfony/mailer/zipball/ab97ef2f7acf0216955f5845484235113047a31d", + "reference": "ab97ef2f7acf0216955f5845484235113047a31d", "shasum": "" }, "require": { @@ -7786,7 +7786,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.3.3" + "source": "https://github.com/symfony/mailer/tree/v7.3.4" }, "funding": [ { @@ -7806,7 +7806,7 @@ "type": "tidelift" } ], - "time": "2025-08-13T11:49:31+00:00" + "time": "2025-09-17T05:51:54+00:00" }, { "name": "symfony/mailgun-mailer", @@ -7879,16 +7879,16 @@ }, { "name": "symfony/mime", - "version": "v7.3.2", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "e0a0f859148daf1edf6c60b398eb40bfc96697d1" + "reference": "b1b828f69cbaf887fa835a091869e55df91d0e35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/e0a0f859148daf1edf6c60b398eb40bfc96697d1", - "reference": "e0a0f859148daf1edf6c60b398eb40bfc96697d1", + "url": "https://api.github.com/repos/symfony/mime/zipball/b1b828f69cbaf887fa835a091869e55df91d0e35", + "reference": "b1b828f69cbaf887fa835a091869e55df91d0e35", "shasum": "" }, "require": { @@ -7943,7 +7943,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.3.2" + "source": "https://github.com/symfony/mime/tree/v7.3.4" }, "funding": [ { @@ -7963,7 +7963,7 @@ "type": "tidelift" } ], - "time": "2025-07-15T13:41:35+00:00" + "time": "2025-09-16T08:38:17+00:00" }, { "name": "symfony/options-resolver", @@ -8867,16 +8867,16 @@ }, { "name": "symfony/process", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "32241012d521e2e8a9d713adb0812bb773b907f1" + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1", - "reference": "32241012d521e2e8a9d713adb0812bb773b907f1", + "url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b", + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b", "shasum": "" }, "require": { @@ -8908,7 +8908,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.3" + "source": "https://github.com/symfony/process/tree/v7.3.4" }, "funding": [ { @@ -8928,7 +8928,7 @@ "type": "tidelift" } ], - "time": "2025-08-18T09:42:54+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/psr-http-message-bridge", @@ -9015,16 +9015,16 @@ }, { "name": "symfony/routing", - "version": "v7.3.2", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "7614b8ca5fa89b9cd233e21b627bfc5774f586e4" + "reference": "8dc648e159e9bac02b703b9fbd937f19ba13d07c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/7614b8ca5fa89b9cd233e21b627bfc5774f586e4", - "reference": "7614b8ca5fa89b9cd233e21b627bfc5774f586e4", + "url": "https://api.github.com/repos/symfony/routing/zipball/8dc648e159e9bac02b703b9fbd937f19ba13d07c", + "reference": "8dc648e159e9bac02b703b9fbd937f19ba13d07c", "shasum": "" }, "require": { @@ -9076,7 +9076,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.3.2" + "source": "https://github.com/symfony/routing/tree/v7.3.4" }, "funding": [ { @@ -9096,7 +9096,7 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:36:08+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/service-contracts", @@ -9183,16 +9183,16 @@ }, { "name": "symfony/string", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c" + "reference": "f96476035142921000338bad71e5247fbc138872" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", - "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", + "url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872", + "reference": "f96476035142921000338bad71e5247fbc138872", "shasum": "" }, "require": { @@ -9207,7 +9207,6 @@ }, "require-dev": { "symfony/emoji": "^7.1", - "symfony/error-handler": "^6.4|^7.0", "symfony/http-client": "^6.4|^7.0", "symfony/intl": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3.0", @@ -9250,7 +9249,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.3" + "source": "https://github.com/symfony/string/tree/v7.3.4" }, "funding": [ { @@ -9270,20 +9269,20 @@ "type": "tidelift" } ], - "time": "2025-08-25T06:35:40+00:00" + "time": "2025-09-11T14:36:48+00:00" }, { "name": "symfony/translation", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "e0837b4cbcef63c754d89a4806575cada743a38d" + "reference": "ec25870502d0c7072d086e8ffba1420c85965174" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/e0837b4cbcef63c754d89a4806575cada743a38d", - "reference": "e0837b4cbcef63c754d89a4806575cada743a38d", + "url": "https://api.github.com/repos/symfony/translation/zipball/ec25870502d0c7072d086e8ffba1420c85965174", + "reference": "ec25870502d0c7072d086e8ffba1420c85965174", "shasum": "" }, "require": { @@ -9350,7 +9349,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v7.3.3" + "source": "https://github.com/symfony/translation/tree/v7.3.4" }, "funding": [ { @@ -9370,7 +9369,7 @@ "type": "tidelift" } ], - "time": "2025-08-01T21:02:37+00:00" + "time": "2025-09-07T11:39:36+00:00" }, { "name": "symfony/translation-contracts", @@ -9526,16 +9525,16 @@ }, { "name": "symfony/var-dumper", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "34d8d4c4b9597347306d1ec8eb4e1319b1e6986f" + "reference": "b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/34d8d4c4b9597347306d1ec8eb4e1319b1e6986f", - "reference": "34d8d4c4b9597347306d1ec8eb4e1319b1e6986f", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb", + "reference": "b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb", "shasum": "" }, "require": { @@ -9589,7 +9588,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.3.3" + "source": "https://github.com/symfony/var-dumper/tree/v7.3.4" }, "funding": [ { @@ -9609,20 +9608,20 @@ "type": "tidelift" } ], - "time": "2025-08-13T11:49:31+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/var-exporter", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "d4dfcd2a822cbedd7612eb6fbd260e46f87b7137" + "reference": "0f020b544a30a7fe8ba972e53ee48a74c0bc87f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/d4dfcd2a822cbedd7612eb6fbd260e46f87b7137", - "reference": "d4dfcd2a822cbedd7612eb6fbd260e46f87b7137", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/0f020b544a30a7fe8ba972e53ee48a74c0bc87f4", + "reference": "0f020b544a30a7fe8ba972e53ee48a74c0bc87f4", "shasum": "" }, "require": { @@ -9670,7 +9669,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v7.3.3" + "source": "https://github.com/symfony/var-exporter/tree/v7.3.4" }, "funding": [ { @@ -9690,7 +9689,7 @@ "type": "tidelift" } ], - "time": "2025-08-18T13:10:53+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "thecodingmachine/safe", @@ -11893,16 +11892,16 @@ }, { "name": "phpunit/phpunit", - "version": "12.3.14", + "version": "12.3.15", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "13e9b2bea9327b094176147250d2c10319a10f5b" + "reference": "b035ee2cd8ecad4091885b61017ebb1d80eb0e57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/13e9b2bea9327b094176147250d2c10319a10f5b", - "reference": "13e9b2bea9327b094176147250d2c10319a10f5b", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b035ee2cd8ecad4091885b61017ebb1d80eb0e57", + "reference": "b035ee2cd8ecad4091885b61017ebb1d80eb0e57", "shasum": "" }, "require": { @@ -11916,7 +11915,7 @@ "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.3", - "phpunit/php-code-coverage": "^12.3.8", + "phpunit/php-code-coverage": "^12.4.0", "phpunit/php-file-iterator": "^6.0.0", "phpunit/php-invoker": "^6.0.0", "phpunit/php-text-template": "^5.0.0", @@ -11970,7 +11969,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.14" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.15" }, "funding": [ { @@ -11994,7 +11993,7 @@ "type": "tidelift" } ], - "time": "2025-09-24T06:34:27+00:00" + "time": "2025-09-28T12:10:54+00:00" }, { "name": "rector/rector", diff --git a/config/firefly.php b/config/firefly.php index da4d0d6233..d3ff9f679b 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -78,8 +78,8 @@ return [ 'running_balance_column' => env('USE_RUNNING_BALANCE', false), // see cer.php for exchange rates feature flag. ], - 'version' => 'develop/2025-09-27', - 'build_time' => 1758945787, + 'version' => 'develop/2025-09-29', + 'build_time' => 1759116036, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 27, diff --git a/package-lock.json b/package-lock.json index 09bb10c0a6..91344ae85e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2589,9 +2589,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.2.tgz", - "integrity": "sha512-o3pcKzJgSGt4d74lSZ+OCnHwkKBeAbFDmbEm5gg70eA8VkyCuC/zV9TwBnmw6VjDlRdF4Pshfb+WE9E6XY1PoQ==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.3.tgz", + "integrity": "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==", "cpu": [ "arm" ], @@ -2603,9 +2603,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.2.tgz", - "integrity": "sha512-cqFSWO5tX2vhC9hJTK8WAiPIm4Q8q/cU8j2HQA0L3E1uXvBYbOZMhE2oFL8n2pKB5sOCHY6bBuHaRwG7TkfJyw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.3.tgz", + "integrity": "sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw==", "cpu": [ "arm64" ], @@ -2617,9 +2617,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.2.tgz", - "integrity": "sha512-vngduywkkv8Fkh3wIZf5nFPXzWsNsVu1kvtLETWxTFf/5opZmflgVSeLgdHR56RQh71xhPhWoOkEBvbehwTlVA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.3.tgz", + "integrity": "sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg==", "cpu": [ "arm64" ], @@ -2631,9 +2631,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.2.tgz", - "integrity": "sha512-h11KikYrUCYTrDj6h939hhMNlqU2fo/X4NB0OZcys3fya49o1hmFaczAiJWVAFgrM1NCP6RrO7lQKeVYSKBPSQ==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.3.tgz", + "integrity": "sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A==", "cpu": [ "x64" ], @@ -2645,9 +2645,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.2.tgz", - "integrity": "sha512-/eg4CI61ZUkLXxMHyVlmlGrSQZ34xqWlZNW43IAU4RmdzWEx0mQJ2mN/Cx4IHLVZFL6UBGAh+/GXhgvGb+nVxw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.3.tgz", + "integrity": "sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ==", "cpu": [ "arm64" ], @@ -2659,9 +2659,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.2.tgz", - "integrity": "sha512-QOWgFH5X9+p+S1NAfOqc0z8qEpJIoUHf7OWjNUGOeW18Mx22lAUOiA9b6r2/vpzLdfxi/f+VWsYjUOMCcYh0Ng==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.3.tgz", + "integrity": "sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A==", "cpu": [ "x64" ], @@ -2673,9 +2673,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.2.tgz", - "integrity": "sha512-kDWSPafToDd8LcBYd1t5jw7bD5Ojcu12S3uT372e5HKPzQt532vW+rGFFOaiR0opxePyUkHrwz8iWYEyH1IIQA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.3.tgz", + "integrity": "sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==", "cpu": [ "arm" ], @@ -2687,9 +2687,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.2.tgz", - "integrity": "sha512-gKm7Mk9wCv6/rkzwCiUC4KnevYhlf8ztBrDRT9g/u//1fZLapSRc+eDZj2Eu2wpJ+0RzUKgtNijnVIB4ZxyL+w==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.3.tgz", + "integrity": "sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==", "cpu": [ "arm" ], @@ -2701,9 +2701,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.2.tgz", - "integrity": "sha512-66lA8vnj5mB/rtDNwPgrrKUOtCLVQypkyDa2gMfOefXK6rcZAxKLO9Fy3GkW8VkPnENv9hBkNOFfGLf6rNKGUg==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.3.tgz", + "integrity": "sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==", "cpu": [ "arm64" ], @@ -2715,9 +2715,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.2.tgz", - "integrity": "sha512-s+OPucLNdJHvuZHuIz2WwncJ+SfWHFEmlC5nKMUgAelUeBUnlB4wt7rXWiyG4Zn07uY2Dd+SGyVa9oyLkVGOjA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.3.tgz", + "integrity": "sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==", "cpu": [ "arm64" ], @@ -2729,9 +2729,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.2.tgz", - "integrity": "sha512-8wTRM3+gVMDLLDdaT6tKmOE3lJyRy9NpJUS/ZRWmLCmOPIJhVyXwjBo+XbrrwtV33Em1/eCTd5TuGJm4+DmYjw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.3.tgz", + "integrity": "sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==", "cpu": [ "loong64" ], @@ -2743,9 +2743,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.2.tgz", - "integrity": "sha512-6yqEfgJ1anIeuP2P/zhtfBlDpXUb80t8DpbYwXQ3bQd95JMvUaqiX+fKqYqUwZXqdJDd8xdilNtsHM2N0cFm6A==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.3.tgz", + "integrity": "sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==", "cpu": [ "ppc64" ], @@ -2757,9 +2757,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.2.tgz", - "integrity": "sha512-sshYUiYVSEI2B6dp4jMncwxbrUqRdNApF2c3bhtLAU0qA8Lrri0p0NauOsTWh3yCCCDyBOjESHMExonp7Nzc0w==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.3.tgz", + "integrity": "sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==", "cpu": [ "riscv64" ], @@ -2771,9 +2771,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.2.tgz", - "integrity": "sha512-duBLgd+3pqC4MMwBrKkFxaZerUxZcYApQVC5SdbF5/e/589GwVvlRUnyqMFbM8iUSb1BaoX/3fRL7hB9m2Pj8Q==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.3.tgz", + "integrity": "sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==", "cpu": [ "riscv64" ], @@ -2785,9 +2785,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.2.tgz", - "integrity": "sha512-tzhYJJidDUVGMgVyE+PmxENPHlvvqm1KILjjZhB8/xHYqAGeizh3GBGf9u6WdJpZrz1aCpIIHG0LgJgH9rVjHQ==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.3.tgz", + "integrity": "sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==", "cpu": [ "s390x" ], @@ -2799,9 +2799,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.2.tgz", - "integrity": "sha512-opH8GSUuVcCSSyHHcl5hELrmnk4waZoVpgn/4FDao9iyE4WpQhyWJ5ryl5M3ocp4qkRuHfyXnGqg8M9oKCEKRA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.3.tgz", + "integrity": "sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==", "cpu": [ "x64" ], @@ -2813,9 +2813,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.2.tgz", - "integrity": "sha512-LSeBHnGli1pPKVJ79ZVJgeZWWZXkEe/5o8kcn23M8eMKCUANejchJbF/JqzM4RRjOJfNRhKJk8FuqL1GKjF5oQ==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.3.tgz", + "integrity": "sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==", "cpu": [ "x64" ], @@ -2827,9 +2827,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.2.tgz", - "integrity": "sha512-uPj7MQ6/s+/GOpolavm6BPo+6CbhbKYyZHUDvZ/SmJM7pfDBgdGisFX3bY/CBDMg2ZO4utfhlApkSfZ92yXw7Q==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.3.tgz", + "integrity": "sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==", "cpu": [ "arm64" ], @@ -2841,9 +2841,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.2.tgz", - "integrity": "sha512-Z9MUCrSgIaUeeHAiNkm3cQyst2UhzjPraR3gYYfOjAuZI7tcFRTOD+4cHLPoS/3qinchth+V56vtqz1Tv+6KPA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.3.tgz", + "integrity": "sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA==", "cpu": [ "arm64" ], @@ -2855,9 +2855,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.2.tgz", - "integrity": "sha512-+GnYBmpjldD3XQd+HMejo+0gJGwYIOfFeoBQv32xF/RUIvccUz20/V6Otdv+57NE70D5pa8W/jVGDoGq0oON4A==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.3.tgz", + "integrity": "sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g==", "cpu": [ "ia32" ], @@ -2869,9 +2869,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.2.tgz", - "integrity": "sha512-ApXFKluSB6kDQkAqZOKXBjiaqdF1BlKi+/eqnYe9Ee7U2K3pUDKsIyr8EYm/QDHTJIM+4X+lI0gJc3TTRhd+dA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.3.tgz", + "integrity": "sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ==", "cpu": [ "x64" ], @@ -2883,9 +2883,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.2.tgz", - "integrity": "sha512-ARz+Bs8kY6FtitYM96PqPEVvPXqEZmPZsSkXvyX19YzDqkCaIlhCieLLMI5hxO9SRZ2XtCtm8wxhy0iJ2jxNfw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.3.tgz", + "integrity": "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA==", "cpu": [ "x64" ], @@ -4075,9 +4075,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.7.tgz", - "integrity": "sha512-bxxN2M3a4d1CRoQC//IqsR5XrLh0IJ8TCv2x6Y9N0nckNz/rTjZB3//GGscZziZOxmjP55rzxg/ze7usFI9FqQ==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.9.tgz", + "integrity": "sha512-hY/u2lxLrbecMEWSB0IpGzGyDyeoMFQhCvZd2jGFSE5I17Fh01sYUBPCJtkWERw7zrac9+cIghxm/ytJa2X8iA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5736,9 +5736,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.224", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.224.tgz", - "integrity": "sha512-kWAoUu/bwzvnhpdZSIc6KUyvkI1rbRXMT0Eq8pKReyOyaPZcctMli+EgvcN1PAvwVc7Tdo4Fxi2PsLNDU05mdg==", + "version": "1.5.227", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.227.tgz", + "integrity": "sha512-ITxuoPfJu3lsNWUi2lBM2PaBPYgH3uqmxut5vmBxgYvyI4AlJ6P3Cai1O76mOrkJCBzq0IxWg/NtqOrpu/0gKA==", "dev": true, "license": "ISC" }, @@ -5820,9 +5820,9 @@ } }, "node_modules/envinfo": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz", - "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.15.0.tgz", + "integrity": "sha512-chR+t7exF6y59kelhXw5I3849nTy7KIRO+ePdLMhCD+JRP/JvmkenDWP7QSFGlsHX+kxGxdDutOPrmj5j1HR6g==", "dev": true, "license": "MIT", "bin": { @@ -10130,9 +10130,9 @@ } }, "node_modules/rollup": { - "version": "4.52.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.2.tgz", - "integrity": "sha512-I25/2QgoROE1vYV+NQ1En9T9UFB9Cmfm2CJ83zZOlaDpvz29wGQSZXWKw7MiNXau7wYgB/T9fVIdIuEQ+KbiiA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.3.tgz", + "integrity": "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==", "dev": true, "license": "MIT", "dependencies": { @@ -10146,28 +10146,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.52.2", - "@rollup/rollup-android-arm64": "4.52.2", - "@rollup/rollup-darwin-arm64": "4.52.2", - "@rollup/rollup-darwin-x64": "4.52.2", - "@rollup/rollup-freebsd-arm64": "4.52.2", - "@rollup/rollup-freebsd-x64": "4.52.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.52.2", - "@rollup/rollup-linux-arm-musleabihf": "4.52.2", - "@rollup/rollup-linux-arm64-gnu": "4.52.2", - "@rollup/rollup-linux-arm64-musl": "4.52.2", - "@rollup/rollup-linux-loong64-gnu": "4.52.2", - "@rollup/rollup-linux-ppc64-gnu": "4.52.2", - "@rollup/rollup-linux-riscv64-gnu": "4.52.2", - "@rollup/rollup-linux-riscv64-musl": "4.52.2", - "@rollup/rollup-linux-s390x-gnu": "4.52.2", - "@rollup/rollup-linux-x64-gnu": "4.52.2", - "@rollup/rollup-linux-x64-musl": "4.52.2", - "@rollup/rollup-openharmony-arm64": "4.52.2", - "@rollup/rollup-win32-arm64-msvc": "4.52.2", - "@rollup/rollup-win32-ia32-msvc": "4.52.2", - "@rollup/rollup-win32-x64-gnu": "4.52.2", - "@rollup/rollup-win32-x64-msvc": "4.52.2", + "@rollup/rollup-android-arm-eabi": "4.52.3", + "@rollup/rollup-android-arm64": "4.52.3", + "@rollup/rollup-darwin-arm64": "4.52.3", + "@rollup/rollup-darwin-x64": "4.52.3", + "@rollup/rollup-freebsd-arm64": "4.52.3", + "@rollup/rollup-freebsd-x64": "4.52.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.3", + "@rollup/rollup-linux-arm-musleabihf": "4.52.3", + "@rollup/rollup-linux-arm64-gnu": "4.52.3", + "@rollup/rollup-linux-arm64-musl": "4.52.3", + "@rollup/rollup-linux-loong64-gnu": "4.52.3", + "@rollup/rollup-linux-ppc64-gnu": "4.52.3", + "@rollup/rollup-linux-riscv64-gnu": "4.52.3", + "@rollup/rollup-linux-riscv64-musl": "4.52.3", + "@rollup/rollup-linux-s390x-gnu": "4.52.3", + "@rollup/rollup-linux-x64-gnu": "4.52.3", + "@rollup/rollup-linux-x64-musl": "4.52.3", + "@rollup/rollup-openharmony-arm64": "4.52.3", + "@rollup/rollup-win32-arm64-msvc": "4.52.3", + "@rollup/rollup-win32-ia32-msvc": "4.52.3", + "@rollup/rollup-win32-x64-gnu": "4.52.3", + "@rollup/rollup-win32-x64-msvc": "4.52.3", "fsevents": "~2.3.2" } }, diff --git a/resources/assets/v1/src/locales/de.json b/resources/assets/v1/src/locales/de.json index 01ba8e3079..b919cfd8a4 100644 --- a/resources/assets/v1/src/locales/de.json +++ b/resources/assets/v1/src/locales/de.json @@ -164,7 +164,7 @@ "title": "Titel", "date": "Datum", "book_date": "Buchungsdatum", - "process_date": "Bearbeitungsdatum", + "process_date": "Wertstellungsdatum", "due_date": "F\u00e4lligkeitstermin", "foreign_amount": "Ausl\u00e4ndischer Betrag", "payment_date": "Zahlungsdatum", From 87d3d14504920ef665be0be901f386ca1b8f92e7 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 1 Oct 2025 20:28:24 +0200 Subject: [PATCH 61/91] Fix #10990 --- .../Models/PiggyBank/UpdateController.php | 4 ---- .../Requests/Models/Transaction/UpdateRequest.php | 10 +++++----- app/Factory/PiggyBankFactory.php | 14 +++++++++----- app/Repositories/PiggyBank/ModifiesPiggyBanks.php | 4 ++-- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/app/Api/V1/Controllers/Models/PiggyBank/UpdateController.php b/app/Api/V1/Controllers/Models/PiggyBank/UpdateController.php index 94a6d2e022..ba53793505 100644 --- a/app/Api/V1/Controllers/Models/PiggyBank/UpdateController.php +++ b/app/Api/V1/Controllers/Models/PiggyBank/UpdateController.php @@ -68,10 +68,6 @@ class UpdateController extends Controller $data = $request->getAll(); $piggyBank = $this->repository->update($piggyBank, $data); - if (array_key_exists('current_amount', $data) && '' !== $data['current_amount']) { - $this->repository->setCurrentAmount($piggyBank, $data['current_amount']); - } - // enrich /** @var User $admin */ $admin = auth()->user(); diff --git a/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php b/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php index 7a2d0a7be4..023a48169e 100644 --- a/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php @@ -64,7 +64,7 @@ class UpdateRequest extends FormRequest */ public function getAll(): array { - app('log')->debug(sprintf('Now in %s', __METHOD__)); + Log::debug(sprintf('Now in %s', __METHOD__)); $this->integerFields = ['order', 'currency_id', 'foreign_currency_id', 'transaction_journal_id', 'source_id', 'destination_id', 'budget_id', 'category_id', 'bill_id', 'recurrence_id']; $this->dateFields = ['date', 'interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date']; $this->textareaFields = ['notes']; @@ -97,7 +97,7 @@ class UpdateRequest extends FormRequest */ private function getTransactionData(): array { - app('log')->debug(sprintf('Now in %s', __METHOD__)); + Log::debug(sprintf('Now in %s', __METHOD__)); $return = []; /** @var null|array $transactions */ @@ -181,7 +181,7 @@ class UpdateRequest extends FormRequest private function getDateData(array $current, array $transaction): array { foreach ($this->dateFields as $fieldName) { - app('log')->debug(sprintf('Now at date field %s', $fieldName)); + Log::debug(sprintf('Now at date field %s', $fieldName)); if (array_key_exists($fieldName, $transaction)) { Log::debug(sprintf('New value: "%s"', $transaction[$fieldName])); $current[$fieldName] = $this->dateFromValue((string) $transaction[$fieldName]); @@ -247,7 +247,7 @@ class UpdateRequest extends FormRequest */ public function rules(): array { - app('log')->debug(sprintf('Now in %s', __METHOD__)); + Log::debug(sprintf('Now in %s', __METHOD__)); $validProtocols = config('firefly.valid_url_protocols'); return [ @@ -330,7 +330,7 @@ class UpdateRequest extends FormRequest */ public function withValidator(Validator $validator): void { - app('log')->debug('Now in withValidator'); + Log::debug('Now in withValidator'); /** @var TransactionGroup $transactionGroup */ $transactionGroup = $this->route()->parameter('transactionGroup'); diff --git a/app/Factory/PiggyBankFactory.php b/app/Factory/PiggyBankFactory.php index 40455001d3..bc6b9f9f8a 100644 --- a/app/Factory/PiggyBankFactory.php +++ b/app/Factory/PiggyBankFactory.php @@ -242,7 +242,7 @@ class PiggyBankFactory } } } - + Log::debug('Looping all accounts.'); /** @var array $info */ foreach ($accounts as $info) { $account = $this->accountRepository->find((int)($info['account_id'] ?? 0)); @@ -251,6 +251,7 @@ class PiggyBankFactory continue; } + Log::debug(sprintf('Working on account #%d', $account->id)); if (array_key_exists('current_amount', $info) && null !== $info['current_amount']) { // an amount is set, first check out if there is a difference with the previous amount. $previous = $toBeLinked[$account->id]['current_amount'] ?? '0'; @@ -258,22 +259,24 @@ class PiggyBankFactory // create event for difference. if (0 !== bccomp($diff, '0')) { + // 2025-10-01 for issue #10990 disable this event. Log::debug(sprintf('[a] Will save event for difference %s (previous value was %s)', $diff, $previous)); - event(new ChangedAmount($piggyBank, $diff, null, null)); + // event(new ChangedAmount($piggyBank, $diff, null, null)); } $toBeLinked[$account->id] = ['current_amount' => $info['current_amount']]; Log::debug(sprintf('[a] Will link account #%d with amount %s', $account->id, $info['current_amount'])); } if (array_key_exists('current_amount', $info) && null === $info['current_amount']) { - // an amount is set, first check out if there is a difference with the previous amount. + // no amount is set, first check out if there is a difference with the previous amount. $previous = $toBeLinked[$account->id]['current_amount'] ?? '0'; $diff = bcsub('0', $previous); // create event for difference. if (0 !== bccomp($diff, '0')) { + // 2025-10-01 for issue #10990 disable this event. Log::debug(sprintf('[b] Will save event for difference %s (previous value was %s)', $diff, $previous)); - event(new ChangedAmount($piggyBank, $diff, null, null)); + // event(new ChangedAmount($piggyBank, $diff, null, null)); } // no amount set, use previous amount or go to ZERO. @@ -282,7 +285,8 @@ class PiggyBankFactory // create event: Log::debug('linkToAccountIds: Trigger change for positive amount [b].'); - event(new ChangedAmount($piggyBank, $toBeLinked[$account->id]['current_amount'] ?? '0', null, null)); + // 2025-10-01 for issue #10990 disable this event. + // event(new ChangedAmount($piggyBank, $toBeLinked[$account->id]['current_amount'] ?? '0', null, null)); } if (!array_key_exists('current_amount', $info)) { $toBeLinked[$account->id] ??= []; diff --git a/app/Repositories/PiggyBank/ModifiesPiggyBanks.php b/app/Repositories/PiggyBank/ModifiesPiggyBanks.php index e63da28927..734cfe8b0f 100644 --- a/app/Repositories/PiggyBank/ModifiesPiggyBanks.php +++ b/app/Repositories/PiggyBank/ModifiesPiggyBanks.php @@ -36,6 +36,7 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups; use FireflyIII\Support\Facades\Amount; +use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Http\Api\ExchangeRateConverter; use Illuminate\Support\Facades\Log; @@ -227,7 +228,6 @@ trait ModifiesPiggyBanks $factory->user = $this->user; // the piggy bank currency is set or updated FIRST, if it exists. - $factory->linkToAccountIds($piggyBank, $data['accounts'] ?? []); @@ -244,7 +244,7 @@ trait ModifiesPiggyBanks // question is, from which account(s) to remove the difference? // solution: just start from the top until there is no more money left to remove. - $this->removeAmountFromAll($piggyBank, app('steam')->positive($difference)); + $this->removeAmountFromAll($piggyBank, Steam::positive($difference)); } // update using name: From 3b85f87502ad98579af577c75d20948d92798b75 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 2 Oct 2025 06:53:23 +0200 Subject: [PATCH 62/91] Stop using "db_version", check versions instead. --- .../Commands/System/OutputsInstructions.php | 59 ++++++------ app/Http/Middleware/Installer.php | 60 +----------- .../FireflyIIIOrg/Update/UpdateRequest.php | 11 +-- app/Support/System/IsOldVersion.php | 94 +++++++++++++++++++ config/firefly.php | 2 +- 5 files changed, 131 insertions(+), 95 deletions(-) create mode 100644 app/Support/System/IsOldVersion.php diff --git a/app/Console/Commands/System/OutputsInstructions.php b/app/Console/Commands/System/OutputsInstructions.php index fb99e99692..1e8c0b6d80 100644 --- a/app/Console/Commands/System/OutputsInstructions.php +++ b/app/Console/Commands/System/OutputsInstructions.php @@ -26,16 +26,18 @@ namespace FireflyIII\Console\Commands\System; use Carbon\Carbon; use FireflyIII\Support\System\GeneratesInstallationId; +use FireflyIII\Support\System\IsOldVersion; use Illuminate\Console\Command; use Random\RandomException; class OutputsInstructions extends Command { use GeneratesInstallationId; + use IsOldVersion; protected $description = 'Instructions in case of upgrade trouble.'; - protected $signature = 'firefly-iii:instructions {task=install}'; + protected $signature = 'firefly-iii:instructions {task=install}'; /** * Execute the console command. @@ -58,26 +60,26 @@ class OutputsInstructions extends Command */ private function updateInstructions(): void { - $version = (string) config('firefly.version'); + $version = (string)config('firefly.version'); /** @var array $config */ - $config = config('upgrade.text.upgrade'); - $text = ''; + $config = config('upgrade.text.upgrade'); + $text = ''; /** @var string $compare */ foreach (array_keys($config) as $compare) { // if string starts with: if (str_starts_with($version, $compare)) { - $text = (string) $config[$compare]; + $text = (string)$config[$compare]; } } // validate some settings. - if ('' === $text && 'local' === (string) config('app.env')) { + if ('' === $text && 'local' === (string)config('app.env')) { $text = 'Please set APP_ENV=production for a safer environment.'; } - $prefix = 'v'; + $prefix = 'v'; if (str_starts_with($version, 'develop') || str_starts_with($version, 'branch')) { $prefix = ''; } @@ -114,8 +116,8 @@ class OutputsInstructions extends Command */ private function showLogo(): void { - $today = Carbon::now()->format('m-d'); - $month = Carbon::now()->format('m'); + $today = Carbon::now()->format('m-d'); + $month = Carbon::now()->format('m'); // variation in colors and effects just because I can! // default is Ukraine flag: $colors = ['blue', 'blue', 'blue', 'yellow', 'yellow', 'yellow', 'default', 'default']; @@ -164,7 +166,7 @@ class OutputsInstructions extends Command { $parts = explode("\n", wordwrap($text)); foreach ($parts as $string) { - $this->line('| '.sprintf('%-77s', $string).'|'); + $this->line('| ' . sprintf('%-77s', $string) . '|'); } } @@ -175,7 +177,7 @@ class OutputsInstructions extends Command { $parts = explode("\n", wordwrap($text)); foreach ($parts as $string) { - $this->info('| '.sprintf('%-77s', $string).'|'); + $this->info('| ' . sprintf('%-77s', $string) . '|'); } } @@ -191,26 +193,26 @@ class OutputsInstructions extends Command */ private function installInstructions(): void { - $version = (string) config('firefly.version'); + $version = (string)config('firefly.version'); /** @var array $config */ - $config = config('upgrade.text.install'); - $text = ''; + $config = config('upgrade.text.install'); + $text = ''; /** @var string $compare */ foreach (array_keys($config) as $compare) { // if string starts with: if (str_starts_with($version, $compare)) { - $text = (string) $config[$compare]; + $text = (string)$config[$compare]; } } // validate some settings. - if ('' === $text && 'local' === (string) config('app.env')) { + if ('' === $text && 'local' === (string)config('app.env')) { $text = 'Please set APP_ENV=production for a safer environment.'; } - $prefix = 'v'; + $prefix = 'v'; if (str_starts_with($version, 'develop')) { $prefix = ''; } @@ -242,14 +244,15 @@ class OutputsInstructions extends Command private function someQuote(): void { - $lines = [ - 'Forgive yourself for not being at peace.', - 'Doesn\'t look like anything to me.', - 'Be proud of what you make.', - 'Be there or forever wonder.', - 'A year from now you will wish you had started today.', + $lines = [ + '"Forgive yourself for not being at peace."', + '"Doesn\'t look like anything to me."', + '"Be proud of what you make."', + '"Be there or forever wonder."', + '"A year from now you will wish you had started today."', + '🇺🇦 Слава Україні!', + '🇺🇦 Slava Ukraini!', ]; - $addQuotes = true; // fuck the Russian aggression in Ukraine. @@ -260,8 +263,7 @@ class OutputsInstructions extends Command // going on, to allow that to happen. if ('ru_RU' === config('firefly.default_language')) { - $addQuotes = false; - $lines = [ + $lines = [ '🇺🇦 Слава Україні!', '🇺🇦 Slava Ukraini!', ]; @@ -272,11 +274,6 @@ class OutputsInstructions extends Command } catch (RandomException) { $random = 0; } - if ($addQuotes) { - $this->line(sprintf(' "%s"', $lines[$random])); - - return; - } $this->line(sprintf(' %s', $lines[$random])); } diff --git a/app/Http/Middleware/Installer.php b/app/Http/Middleware/Installer.php index f7d38232ca..79f9494929 100644 --- a/app/Http/Middleware/Installer.php +++ b/app/Http/Middleware/Installer.php @@ -26,6 +26,8 @@ namespace FireflyIII\Http\Middleware; use Closure; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Support\FireflyConfig; +use FireflyIII\Support\System\IsOldVersion; use FireflyIII\Support\System\OAuthKeys; use Illuminate\Database\QueryException; use Illuminate\Http\Request; @@ -37,6 +39,7 @@ use Illuminate\Support\Facades\Log; */ class Installer { + use IsOldVersion; /** * Handle an incoming request. * @@ -65,7 +68,7 @@ class Installer // run installer when no tables are present, // or when old scheme version // or when old firefly version - if ($this->hasNoTables() || $this->oldDBVersion() || $this->oldVersion()) { + if ($this->hasNoTables() || $this->isOldVersionInstalled()) { return response()->redirectTo(route('installer.index')); } OAuthKeys::verifyKeysRoutine(); @@ -126,59 +129,4 @@ class Installer { return false !== stripos($message, 'Base table or view not found'); } - - /** - * Check if the "db_version" variable is correct. - */ - private function oldDBVersion(): bool - { - // older version in config than database? - $configVersion = (int) config('firefly.db_version'); - $dbVersion = (int) app('fireflyconfig')->getFresh('db_version', 1)->data; - if ($configVersion > $dbVersion) { - Log::warning( - sprintf( - 'The current configured version (%d) is older than the required version (%d). Redirect to migrate routine.', - $dbVersion, - $configVersion - ) - ); - - return true; - } - - // Log::info(sprintf('Configured DB version (%d) equals expected DB version (%d)', $dbVersion, $configVersion)); - - return false; - } - - /** - * Check if the "firefly_version" variable is correct. - */ - private function oldVersion(): bool - { - // version compare thing. - $configVersion = (string) config('firefly.version'); - $dbVersion = (string) app('fireflyconfig')->getFresh('ff3_version', '1.0')->data; - if (str_starts_with($configVersion, 'develop')) { - Log::debug('Skipping version check for develop version.'); - - return false; - } - if (1 === version_compare($configVersion, $dbVersion)) { - Log::warning( - sprintf( - 'The current configured Firefly III version (%s) is older than the required version (%s). Redirect to migrate routine.', - $dbVersion, - $configVersion - ) - ); - - return true; - } - - // Log::info(sprintf('Installed Firefly III version (%s) equals expected Firefly III version (%s)', $dbVersion, $configVersion)); - - return false; - } } diff --git a/app/Services/FireflyIIIOrg/Update/UpdateRequest.php b/app/Services/FireflyIIIOrg/Update/UpdateRequest.php index 9516071ddb..b2c806a966 100644 --- a/app/Services/FireflyIIIOrg/Update/UpdateRequest.php +++ b/app/Services/FireflyIIIOrg/Update/UpdateRequest.php @@ -26,6 +26,7 @@ namespace FireflyIII\Services\FireflyIIIOrg\Update; use Carbon\Carbon; use FireflyIII\Events\NewVersionAvailable; +use FireflyIII\Support\System\IsOldVersion; use GuzzleHttp\Client; use GuzzleHttp\Exception\GuzzleException; use Illuminate\Support\Facades\Log; @@ -38,6 +39,7 @@ use function Safe\json_decode; */ class UpdateRequest implements UpdateRequestInterface { + use IsOldVersion; public function getUpdateInformation(string $channel): array { Log::debug(sprintf('Now in getUpdateInformation(%s)', $channel)); @@ -183,20 +185,15 @@ class UpdateRequest implements UpdateRequestInterface private function parseResultDevelop(string $current, string $latest, array $information): array { Log::debug(sprintf('User is running develop version "%s"', $current)); - $parts = explode('/', $current); + $compare = $this->compareDevelopVersions($current, $latest); $return = []; - /** @var Carbon $devDate */ - $devDate = Carbon::createFromFormat('Y-m-d', $parts[1]); - - if ($devDate->lte($information['date'])) { - Log::debug(sprintf('This development release is older, release = %s, latest version %s = %s', $devDate->format('Y-m-d'), $latest, $information['date']->format('Y-m-d'))); + if (-1 === $compare) { $return['level'] = 'info'; $return['message'] = (string) trans('firefly.update_current_dev_older', ['version' => $current, 'new_version' => $latest]); return $return; } - Log::debug(sprintf('This development release is newer, release = %s, latest version %s = %s', $devDate->format('Y-m-d'), $latest, $information['date']->format('Y-m-d'))); $return['level'] = 'info'; $return['message'] = (string) trans('firefly.update_current_dev_newer', ['version' => $current, 'new_version' => $latest]); diff --git a/app/Support/System/IsOldVersion.php b/app/Support/System/IsOldVersion.php new file mode 100644 index 0000000000..175a8ed7fa --- /dev/null +++ b/app/Support/System/IsOldVersion.php @@ -0,0 +1,94 @@ +. + */ + +namespace FireflyIII\Support\System; + +use Carbon\Carbon; +use FireflyIII\Support\Facades\FireflyConfig; +use Illuminate\Support\Facades\Log; + +trait IsOldVersion +{ + + /** + * By default, version_compare() returns -1 if the first version is lower than the second, 0 if they are equal, and + * 1 if the second is lower. + */ + protected function compareDevelopVersions(string $current, string $latest): int + { + $currentParts = explode('/', $current); + $latestParts = explode('/', $latest); + if (2 !== count($currentParts) || 2 !== count($latestParts)) { + Log::error(sprintf('Version "%s" or "%s" is not a valid develop-version.', $current, $latest)); + return 0; + } + + $currentDate = Carbon::createFromFormat('!Y-m-d', $currentParts[1]); + $latestDate = Carbon::createFromFormat('!Y-m-d', $latestParts[1]); + + if ($currentDate->lt($latestDate)) { + Log::debug(sprintf('This current version is older, current = %s, latest version %s.', $current, $latest)); + return -1; + } + if ($currentDate->gt($latestDate)) { + Log::debug(sprintf('This current version is newer, current = %s, latest version %s.', $current, $latest)); + return 1; + } + Log::debug(sprintf('This current version is of the same age, current = %s, latest version %s.', $current, $latest)); + + return 0; + } + + /** + * Check if the "firefly_version" variable is correct. + */ + protected function isOldVersionInstalled(): bool + { + // version compare thing. + $configVersion = (string)config('firefly.version'); + $dbVersion = (string)FireflyConfig::getFresh('ff3_version', '1.0')->data; + $compare = 0; + // compare develop to develop + if (str_starts_with($configVersion, 'develop') && str_starts_with($dbVersion, 'develop')) { + $compare = $this->compareDevelopVersions($configVersion, $dbVersion); + } + // user has develop installed, goes to normal version. + if (!str_starts_with($configVersion, 'develop') && str_starts_with($dbVersion, 'develop')) { + return true; + } + + // user has normal, goes to develop version. + if (str_starts_with($configVersion, 'develop') && !str_starts_with($dbVersion, 'develop')) { + return true; + } + + // compare normal with normal. + if (!str_starts_with($configVersion, 'develop') && !str_starts_with($dbVersion, 'develop')) { + $compare = version_compare($configVersion, $dbVersion); + } + if (-1 === $compare) { + Log::warning(sprintf('The current configured Firefly III version (%s) is older than the required version (%s). Redirect to migrate routine.', $dbVersion, $configVersion)); + + return true; + } + return false; + } +} diff --git a/config/firefly.php b/config/firefly.php index d3ff9f679b..7b1af7a67a 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -81,7 +81,7 @@ return [ 'version' => 'develop/2025-09-29', 'build_time' => 1759116036, 'api_version' => '2.1.0', // field is no longer used. - 'db_version' => 27, + 'db_version' => 28, // generic settings 'maxUploadSize' => 1073741824, // 1 GB From 62b9f2785f3ad8994765f875c5bc40a0a456be6a Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 2 Oct 2025 06:55:09 +0200 Subject: [PATCH 63/91] Ignore db version in scripts. --- app/Console/Commands/System/SetsLatestVersion.php | 4 ++-- app/Console/Commands/Upgrade/UpgradesDatabase.php | 5 ++--- app/Http/Controllers/DebugController.php | 1 - app/Http/Controllers/System/InstallController.php | 6 ++---- config/firefly.php | 2 +- resources/views/partials/debug-table.twig | 3 +-- 6 files changed, 8 insertions(+), 13 deletions(-) diff --git a/app/Console/Commands/System/SetsLatestVersion.php b/app/Console/Commands/System/SetsLatestVersion.php index f6207ee15a..3c6f6b5244 100644 --- a/app/Console/Commands/System/SetsLatestVersion.php +++ b/app/Console/Commands/System/SetsLatestVersion.php @@ -25,6 +25,7 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\System; use FireflyIII\Console\Commands\ShowsFriendlyMessages; +use FireflyIII\Support\Facades\FireflyConfig; use Illuminate\Console\Command; class SetsLatestVersion extends Command @@ -45,8 +46,7 @@ class SetsLatestVersion extends Command return 0; } - app('fireflyconfig')->set('db_version', config('firefly.db_version')); - app('fireflyconfig')->set('ff3_version', config('firefly.version')); + FireflyConfig::set('ff3_version', config('firefly.version')); $this->friendlyInfo('Updated version.'); return 0; diff --git a/app/Console/Commands/Upgrade/UpgradesDatabase.php b/app/Console/Commands/Upgrade/UpgradesDatabase.php index 92429a9a36..c2f6912b5a 100644 --- a/app/Console/Commands/Upgrade/UpgradesDatabase.php +++ b/app/Console/Commands/Upgrade/UpgradesDatabase.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Upgrade; +use FireflyIII\Support\Facades\FireflyConfig; use Illuminate\Support\Facades\Log; use Safe\Exceptions\InfoException; @@ -86,10 +87,8 @@ class UpgradesDatabase extends Command $this->friendlyLine(sprintf('Now executing %s', $command)); $this->call($command, $args); } - // set new DB version. - app('fireflyconfig')->set('db_version', (int) config('firefly.db_version')); // index will set FF3 version. - app('fireflyconfig')->set('ff3_version', (string) config('firefly.version')); + FireflyConfig::set('ff3_version', (string) config('firefly.version')); return 0; } diff --git a/app/Http/Controllers/DebugController.php b/app/Http/Controllers/DebugController.php index 377bacd735..9138daf037 100644 --- a/app/Http/Controllers/DebugController.php +++ b/app/Http/Controllers/DebugController.php @@ -184,7 +184,6 @@ class DebugController extends Controller $currentDriver = DB::getDriverName(); return [ - 'db_version' => app('fireflyconfig')->get('db_version', 1)->data, 'php_version' => PHP_VERSION, 'php_os' => PHP_OS, 'uname' => php_uname('m'), diff --git a/app/Http/Controllers/System/InstallController.php b/app/Http/Controllers/System/InstallController.php index e2acfb680f..cd800053d4 100644 --- a/app/Http/Controllers/System/InstallController.php +++ b/app/Http/Controllers/System/InstallController.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\System; +use FireflyIII\Support\Facades\FireflyConfig; use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Cache; use Exception; @@ -81,10 +82,7 @@ class InstallController extends Controller { app('view')->share('FF_VERSION', config('firefly.version')); // index will set FF3 version. - app('fireflyconfig')->set('ff3_version', (string) config('firefly.version')); - - // set new DB version. - app('fireflyconfig')->set('db_version', (int) config('firefly.db_version')); + FireflyConfig::set('ff3_version', (string) config('firefly.version')); return view('install.index'); } diff --git a/config/firefly.php b/config/firefly.php index 7b1af7a67a..e4e4f573c1 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -81,7 +81,7 @@ return [ 'version' => 'develop/2025-09-29', 'build_time' => 1759116036, 'api_version' => '2.1.0', // field is no longer used. - 'db_version' => 28, + 'db_version' => 28, // field is no longer used. // generic settings 'maxUploadSize' => 1073741824, // 1 GB diff --git a/resources/views/partials/debug-table.twig b/resources/views/partials/debug-table.twig index 6dd268a04e..2e498a162a 100644 --- a/resources/views/partials/debug-table.twig +++ b/resources/views/partials/debug-table.twig @@ -12,8 +12,7 @@ {# Firefly III version #} Firefly III - {% if FF_IS_DEVELOP %}{% endif %}{% if not FF_IS_DEVELOP %}v{% endif %}{{ FF_VERSION }} / #{{ system.db_version }} (expects #{{ config('firefly.db_version') }}) - + {% if FF_IS_DEVELOP %}{% endif %}{% if not FF_IS_DEVELOP %}v{% endif %}{{ FF_VERSION }} {# PHP version + settings #} From c5cf52941388b60e565279e72138b6dc7bf15c4a Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 2 Oct 2025 06:59:56 +0200 Subject: [PATCH 64/91] Update changelog. --- changelog.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index aecba981e0..f4123d6829 100644 --- a/changelog.md +++ b/changelog.md @@ -5,16 +5,31 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## 6.4.1 - 2025-09-15 +### Added + +- #10979 + ### Fixed - Fixed a missing filter from [issue 10803](https://github.com/firefly-iii/firefly-iii/issues/10803). +- #10833 +- #10854 - #10891 +- #10916 - #10920 - #10921 -- #10833 +- #10924 +- #10938 +- #10940 +- #10954 +- #10956 +- #10960 +- #10974 +- #10990 ### API +- #10803 - #10908 From d1bae875f798da8837a635691160492b75ccdf23 Mon Sep 17 00:00:00 2001 From: JC5 Date: Thu, 2 Oct 2025 07:04:45 +0200 Subject: [PATCH 65/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-10-02?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Commands/System/OutputsInstructions.php | 22 +-- app/Factory/PiggyBankFactory.php | 1 + app/Http/Middleware/Installer.php | 2 +- .../FireflyIIIOrg/Update/UpdateRequest.php | 3 +- .../Http/Controllers/PeriodOverview.php | 28 ++-- app/Support/System/IsOldVersion.php | 11 +- composer.lock | 92 ++-------- config/firefly.php | 4 +- package-lock.json | 158 ++++++------------ 9 files changed, 101 insertions(+), 220 deletions(-) diff --git a/app/Console/Commands/System/OutputsInstructions.php b/app/Console/Commands/System/OutputsInstructions.php index 1e8c0b6d80..877576e4a4 100644 --- a/app/Console/Commands/System/OutputsInstructions.php +++ b/app/Console/Commands/System/OutputsInstructions.php @@ -37,7 +37,7 @@ class OutputsInstructions extends Command protected $description = 'Instructions in case of upgrade trouble.'; - protected $signature = 'firefly-iii:instructions {task=install}'; + protected $signature = 'firefly-iii:instructions {task=install}'; /** * Execute the console command. @@ -63,8 +63,8 @@ class OutputsInstructions extends Command $version = (string)config('firefly.version'); /** @var array $config */ - $config = config('upgrade.text.upgrade'); - $text = ''; + $config = config('upgrade.text.upgrade'); + $text = ''; /** @var string $compare */ foreach (array_keys($config) as $compare) { @@ -79,7 +79,7 @@ class OutputsInstructions extends Command $text = 'Please set APP_ENV=production for a safer environment.'; } - $prefix = 'v'; + $prefix = 'v'; if (str_starts_with($version, 'develop') || str_starts_with($version, 'branch')) { $prefix = ''; } @@ -116,8 +116,8 @@ class OutputsInstructions extends Command */ private function showLogo(): void { - $today = Carbon::now()->format('m-d'); - $month = Carbon::now()->format('m'); + $today = Carbon::now()->format('m-d'); + $month = Carbon::now()->format('m'); // variation in colors and effects just because I can! // default is Ukraine flag: $colors = ['blue', 'blue', 'blue', 'yellow', 'yellow', 'yellow', 'default', 'default']; @@ -166,7 +166,7 @@ class OutputsInstructions extends Command { $parts = explode("\n", wordwrap($text)); foreach ($parts as $string) { - $this->line('| ' . sprintf('%-77s', $string) . '|'); + $this->line('| '.sprintf('%-77s', $string).'|'); } } @@ -177,7 +177,7 @@ class OutputsInstructions extends Command { $parts = explode("\n", wordwrap($text)); foreach ($parts as $string) { - $this->info('| ' . sprintf('%-77s', $string) . '|'); + $this->info('| '.sprintf('%-77s', $string).'|'); } } @@ -196,8 +196,8 @@ class OutputsInstructions extends Command $version = (string)config('firefly.version'); /** @var array $config */ - $config = config('upgrade.text.install'); - $text = ''; + $config = config('upgrade.text.install'); + $text = ''; /** @var string $compare */ foreach (array_keys($config) as $compare) { @@ -212,7 +212,7 @@ class OutputsInstructions extends Command $text = 'Please set APP_ENV=production for a safer environment.'; } - $prefix = 'v'; + $prefix = 'v'; if (str_starts_with($version, 'develop')) { $prefix = ''; } diff --git a/app/Factory/PiggyBankFactory.php b/app/Factory/PiggyBankFactory.php index bc6b9f9f8a..b31056b10f 100644 --- a/app/Factory/PiggyBankFactory.php +++ b/app/Factory/PiggyBankFactory.php @@ -243,6 +243,7 @@ class PiggyBankFactory } } Log::debug('Looping all accounts.'); + /** @var array $info */ foreach ($accounts as $info) { $account = $this->accountRepository->find((int)($info['account_id'] ?? 0)); diff --git a/app/Http/Middleware/Installer.php b/app/Http/Middleware/Installer.php index 79f9494929..852c523c4c 100644 --- a/app/Http/Middleware/Installer.php +++ b/app/Http/Middleware/Installer.php @@ -26,7 +26,6 @@ namespace FireflyIII\Http\Middleware; use Closure; use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Support\FireflyConfig; use FireflyIII\Support\System\IsOldVersion; use FireflyIII\Support\System\OAuthKeys; use Illuminate\Database\QueryException; @@ -40,6 +39,7 @@ use Illuminate\Support\Facades\Log; class Installer { use IsOldVersion; + /** * Handle an incoming request. * diff --git a/app/Services/FireflyIIIOrg/Update/UpdateRequest.php b/app/Services/FireflyIIIOrg/Update/UpdateRequest.php index b2c806a966..ad55aee903 100644 --- a/app/Services/FireflyIIIOrg/Update/UpdateRequest.php +++ b/app/Services/FireflyIIIOrg/Update/UpdateRequest.php @@ -40,6 +40,7 @@ use function Safe\json_decode; class UpdateRequest implements UpdateRequestInterface { use IsOldVersion; + public function getUpdateInformation(string $channel): array { Log::debug(sprintf('Now in getUpdateInformation(%s)', $channel)); @@ -185,7 +186,7 @@ class UpdateRequest implements UpdateRequestInterface private function parseResultDevelop(string $current, string $latest, array $information): array { Log::debug(sprintf('User is running develop version "%s"', $current)); - $compare = $this->compareDevelopVersions($current, $latest); + $compare = $this->compareDevelopVersions($current, $latest); $return = []; if (-1 === $compare) { diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index 5209eab2fe..ddbe866e84 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -263,13 +263,13 @@ trait PeriodOverview $entry = [ - 'title' => $title, - 'route' => route(sprintf('%s.no-%s', Str::plural($model), $model), [$start->format('Y-m-d'), $end->format('Y-m-d')]), - 'total_transactions' => 0, - 'spent' => [], - 'earned' => [], - 'transferred' => [], - ]; + 'title' => $title, + 'route' => route(sprintf('%s.no-%s', Str::plural($model), $model), [$start->format('Y-m-d'), $end->format('Y-m-d')]), + 'total_transactions' => 0, + 'spent' => [], + 'earned' => [], + 'transferred' => [], + ]; $grouped = []; /** @var PeriodStatistic $statistic */ @@ -515,13 +515,13 @@ trait PeriodOverview } $entries[] = [ - 'title' => $title, - 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'title' => $title, + 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; ++$loops; } diff --git a/app/Support/System/IsOldVersion.php b/app/Support/System/IsOldVersion.php index 175a8ed7fa..2499debbe8 100644 --- a/app/Support/System/IsOldVersion.php +++ b/app/Support/System/IsOldVersion.php @@ -1,4 +1,6 @@ lt($latestDate)) { Log::debug(sprintf('This current version is older, current = %s, latest version %s.', $current, $latest)); + return -1; } if ($currentDate->gt($latestDate)) { Log::debug(sprintf('This current version is newer, current = %s, latest version %s.', $current, $latest)); + return 1; } Log::debug(sprintf('This current version is of the same age, current = %s, latest version %s.', $current, $latest)); @@ -89,6 +93,7 @@ trait IsOldVersion return true; } + return false; } } diff --git a/composer.lock b/composer.lock index f7341ff02a..bf27259208 100644 --- a/composer.lock +++ b/composer.lock @@ -1878,16 +1878,16 @@ }, { "name": "laravel/framework", - "version": "v12.31.1", + "version": "v12.32.5", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "281b711710c245dd8275d73132e92635be3094df" + "reference": "77b2740391cd2a825ba59d6fada45e9b8b9bcc5a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/281b711710c245dd8275d73132e92635be3094df", - "reference": "281b711710c245dd8275d73132e92635be3094df", + "url": "https://api.github.com/repos/laravel/framework/zipball/77b2740391cd2a825ba59d6fada45e9b8b9bcc5a", + "reference": "77b2740391cd2a825ba59d6fada45e9b8b9bcc5a", "shasum": "" }, "require": { @@ -1915,7 +1915,6 @@ "monolog/monolog": "^3.0", "nesbot/carbon": "^3.8.4", "nunomaduro/termwind": "^2.0", - "phiki/phiki": "^2.0.0", "php": "^8.2", "psr/container": "^1.1.1|^2.0.1", "psr/log": "^1.0|^2.0|^3.0", @@ -2094,7 +2093,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-09-23T15:33:04+00:00" + "time": "2025-09-30T17:39:22+00:00" }, { "name": "laravel/passport", @@ -2812,16 +2811,16 @@ }, { "name": "league/csv", - "version": "9.25.0", + "version": "9.26.0", "source": { "type": "git", "url": "https://github.com/thephpleague/csv.git", - "reference": "f856f532866369fb1debe4e7c5a1db185f40ef86" + "reference": "7fce732754d043f3938899e5183e2d0f3d31b571" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/csv/zipball/f856f532866369fb1debe4e7c5a1db185f40ef86", - "reference": "f856f532866369fb1debe4e7c5a1db185f40ef86", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/7fce732754d043f3938899e5183e2d0f3d31b571", + "reference": "7fce732754d043f3938899e5183e2d0f3d31b571", "shasum": "" }, "require": { @@ -2899,7 +2898,7 @@ "type": "github" } ], - "time": "2025-09-11T08:29:08+00:00" + "time": "2025-10-01T11:24:54+00:00" }, { "name": "league/event", @@ -4352,77 +4351,6 @@ }, "time": "2020-10-15T08:29:30+00:00" }, - { - "name": "phiki/phiki", - "version": "v2.0.4", - "source": { - "type": "git", - "url": "https://github.com/phikiphp/phiki.git", - "reference": "160785c50c01077780ab217e5808f00ab8f05a13" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phikiphp/phiki/zipball/160785c50c01077780ab217e5808f00ab8f05a13", - "reference": "160785c50c01077780ab217e5808f00ab8f05a13", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "league/commonmark": "^2.5.3", - "php": "^8.2", - "psr/simple-cache": "^3.0" - }, - "require-dev": { - "illuminate/support": "^11.45", - "laravel/pint": "^1.18.1", - "orchestra/testbench": "^9.15", - "pestphp/pest": "^3.5.1", - "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^2.0", - "symfony/var-dumper": "^7.1.6" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Phiki\\Adapters\\Laravel\\PhikiServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "Phiki\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ryan Chandler", - "email": "support@ryangjchandler.co.uk", - "homepage": "https://ryangjchandler.co.uk", - "role": "Developer" - } - ], - "description": "Syntax highlighting using TextMate grammars in PHP.", - "support": { - "issues": "https://github.com/phikiphp/phiki/issues", - "source": "https://github.com/phikiphp/phiki/tree/v2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sponsors/ryangjchandler", - "type": "github" - }, - { - "url": "https://buymeacoffee.com/ryangjchandler", - "type": "other" - } - ], - "time": "2025-09-20T17:21:02+00:00" - }, { "name": "php-http/client-common", "version": "2.7.2", diff --git a/config/firefly.php b/config/firefly.php index e4e4f573c1..24c618740a 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -78,8 +78,8 @@ return [ 'running_balance_column' => env('USE_RUNNING_BALANCE', false), // see cer.php for exchange rates feature flag. ], - 'version' => 'develop/2025-09-29', - 'build_time' => 1759116036, + 'version' => 'develop/2025-10-02', + 'build_time' => 1759381368, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 28, // field is no longer used. diff --git a/package-lock.json b/package-lock.json index 91344ae85e..77e6898b78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2135,9 +2135,9 @@ } }, "node_modules/@fortawesome/fontawesome-free": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-7.0.1.tgz", - "integrity": "sha512-RLmb9U6H2rJDnGxEqXxzy7ANPrQz7WK2/eTjdZqyU9uRU5W+FkAec9uU5gTYzFBH7aoXIw2WTJSCJR4KPlReQw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-7.1.0.tgz", + "integrity": "sha512-+WxNld5ZCJHvPQCr/GnzCTVREyStrAJjisUPtUxG5ngDA8TMlPnKp6dddlTpai4+1GNmltAeuk1hJEkBohwZYA==", "license": "(CC-BY-4.0 AND OFL-1.1 AND MIT)", "engines": { "node": ">=6" @@ -3173,13 +3173,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.5.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.2.tgz", - "integrity": "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==", + "version": "24.6.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.2.tgz", + "integrity": "sha512-d2L25Y4j+W3ZlNAeMKcy7yDsK425ibcAOO2t7aPTz6gNMH0z2GThtwENCDc0d/Pw9wgyRqE5Px1wkV7naz8ang==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.12.0" + "undici-types": "~7.13.0" } }, "node_modules/@types/node-forge": { @@ -3898,16 +3898,6 @@ "dev": true, "license": "MIT" }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/autoprefixer": { "version": "10.4.21", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", @@ -4075,9 +4065,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.9.tgz", - "integrity": "sha512-hY/u2lxLrbecMEWSB0IpGzGyDyeoMFQhCvZd2jGFSE5I17Fh01sYUBPCJtkWERw7zrac9+cIghxm/ytJa2X8iA==", + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.10.tgz", + "integrity": "sha512-uLfgBi+7IBNay8ECBO2mVMGZAc1VgZWEChxm4lv+TobGdG82LnXMjuNGo/BSSZZL4UmkWhxEHP2f5ziLNwGWMA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4360,9 +4350,9 @@ } }, "node_modules/browserslist": { - "version": "4.26.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", - "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==", + "version": "4.26.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.3.tgz", + "integrity": "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==", "dev": true, "funding": [ { @@ -4380,9 +4370,9 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.8.3", - "caniuse-lite": "^1.0.30001741", - "electron-to-chromium": "^1.5.218", + "baseline-browser-mapping": "^2.8.9", + "caniuse-lite": "^1.0.30001746", + "electron-to-chromium": "^1.5.227", "node-releases": "^2.0.21", "update-browserslist-db": "^1.1.3" }, @@ -4521,9 +4511,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001745", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001745.tgz", - "integrity": "sha512-ywt6i8FzvdgrrrGbr1jZVObnVv6adj+0if2/omv9cmR2oiZs30zL4DIyaptKcbOrBdOIc74QTMoJvSE2QHh5UQ==", + "version": "1.0.30001746", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001746.tgz", + "integrity": "sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA==", "dev": true, "funding": [ { @@ -5061,9 +5051,9 @@ } }, "node_modules/cross-env": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.0.0.tgz", - "integrity": "sha512-aU8qlEK/nHYtVuN4p7UQgAwVljzMg8hB4YK5ThRqD2l/ziSnryncPNn7bMLt5cFYsKVKBh8HqLqyCoTupEUu7Q==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz", + "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==", "dev": true, "license": "MIT", "dependencies": { @@ -5736,9 +5726,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.227", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.227.tgz", - "integrity": "sha512-ITxuoPfJu3lsNWUi2lBM2PaBPYgH3uqmxut5vmBxgYvyI4AlJ6P3Cai1O76mOrkJCBzq0IxWg/NtqOrpu/0gKA==", + "version": "1.5.228", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.228.tgz", + "integrity": "sha512-nxkiyuqAn4MJ1QbobwqJILiDtu/jk14hEAWaMiJmNPh1Z+jqoFlBFZjdXwLWGeVSeu9hGLg6+2G9yJaW8rBIFA==", "dev": true, "license": "ISC" }, @@ -7088,9 +7078,9 @@ } }, "node_modules/i18next": { - "version": "25.5.2", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.5.2.tgz", - "integrity": "sha512-lW8Zeh37i/o0zVr+NoCHfNnfvVw+M6FQbRp36ZZ/NyHDJ3NJVpp2HhAUyU9WafL5AssymNoOjMRB48mmx2P6Hw==", + "version": "25.5.3", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.5.3.tgz", + "integrity": "sha512-joFqorDeQ6YpIXni944upwnuHBf5IoPMuqAchGVeQLdWC2JOjxgM9V8UGLhNIIH/Q8QleRxIi0BSRQehSrDLcg==", "funding": [ { "type": "individual", @@ -8653,16 +8643,6 @@ "dev": true, "license": "MIT" }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -8818,9 +8798,9 @@ } }, "node_modules/patch-package": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz", - "integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.1.tgz", + "integrity": "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw==", "dev": true, "license": "MIT", "dependencies": { @@ -8829,15 +8809,14 @@ "ci-info": "^3.7.0", "cross-spawn": "^7.0.3", "find-yarn-workspace-root": "^2.0.0", - "fs-extra": "^9.0.0", + "fs-extra": "^10.0.0", "json-stable-stringify": "^1.0.2", "klaw-sync": "^6.0.0", "minimist": "^1.2.6", "open": "^7.4.2", - "rimraf": "^2.6.3", "semver": "^7.5.3", "slash": "^2.0.0", - "tmp": "^0.0.33", + "tmp": "^0.2.4", "yaml": "^2.2.2" }, "bin": { @@ -8848,22 +8827,6 @@ "npm": ">5" } }, - "node_modules/patch-package/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/patch-package/node_modules/slash": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", @@ -10086,9 +10049,9 @@ } }, "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "license": "ISC", @@ -10097,6 +10060,9 @@ }, "bin": { "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/ripemd160": { @@ -11214,16 +11180,13 @@ } }, "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", "dev": true, "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, "engines": { - "node": ">=0.6.0" + "node": ">=14.14" } }, "node_modules/to-arraybuffer": { @@ -11343,9 +11306,9 @@ } }, "node_modules/undici-types": { - "version": "7.12.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz", - "integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.13.0.tgz", + "integrity": "sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ==", "dev": true, "license": "MIT" }, @@ -11863,9 +11826,9 @@ "license": "BSD-2-Clause" }, "node_modules/webpack": { - "version": "5.101.3", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.101.3.tgz", - "integrity": "sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A==", + "version": "5.102.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.102.0.tgz", + "integrity": "sha512-hUtqAR3ZLVEYDEABdBioQCIqSoguHbFn1K7WlPPWSuXmx0031BD73PSE35jKyftdSh4YLDoQNgK4pqBt5Q82MA==", "dev": true, "license": "MIT", "dependencies": { @@ -11877,7 +11840,7 @@ "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.15.0", "acorn-import-phases": "^1.0.3", - "browserslist": "^4.24.0", + "browserslist": "^4.24.5", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.17.3", "es-module-lexer": "^1.2.1", @@ -11890,9 +11853,9 @@ "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^4.3.2", - "tapable": "^2.1.1", + "tapable": "^2.2.3", "terser-webpack-plugin": "^5.3.11", - "watchpack": "^2.4.1", + "watchpack": "^2.4.4", "webpack-sources": "^3.3.3" }, "bin": { @@ -12155,23 +12118,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/webpack-dev-server/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/webpack-dev-server/node_modules/schema-utils": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", From a6ba75d5288ddd6cba69c90aa036c2ec722338da Mon Sep 17 00:00:00 2001 From: JC5 Date: Thu, 2 Oct 2025 07:39:29 +0200 Subject: [PATCH 66/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-10-02?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- changelog.md | 34 +++++++++++++++++----------------- config/firefly.php | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/changelog.md b/changelog.md index f4123d6829..a165d12d9b 100644 --- a/changelog.md +++ b/changelog.md @@ -7,30 +7,30 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added -- #10979 +- [PR 10979](https://github.com/firefly-iii/firefly-iii/pull/10979) (Add Kazakhstani Tenge (KZT) currency) reported by @maksimkurb ### Fixed - Fixed a missing filter from [issue 10803](https://github.com/firefly-iii/firefly-iii/issues/10803). -- #10833 -- #10854 -- #10891 -- #10916 -- #10920 -- #10921 -- #10924 -- #10938 -- #10940 -- #10954 -- #10956 -- #10960 -- #10974 -- #10990 +- [Issue 10833](https://github.com/firefly-iii/firefly-iii/issues/10833) (Can't open transaction after assigning a tag to it) reported by @zynexiz +- [Issue 10854](https://github.com/firefly-iii/firefly-iii/issues/10854) (string / null in budget causes budget page to not render) reported by @4e868df3 +- [Discussion 10891](https://github.com/orgs/firefly-iii/discussions/10891) (User group id is null when downloading new exchange rates) started by @dakennguyen +- [Discussion 10916](https://github.com/orgs/firefly-iii/discussions/10916) (Errors/Warnings in Logs after Batch API Import) started by @Mr-Kanister +- [Issue 10920](https://github.com/firefly-iii/firefly-iii/issues/10920) (Liability transaction with same source and destination possible) reported by @Mr-Kanister +- [Issue 10921](https://github.com/firefly-iii/firefly-iii/issues/10921) (Transaction type between asset and liability not correctly enforced using API) reported by @Mr-Kanister +- [Issue 10924](https://github.com/firefly-iii/firefly-iii/issues/10924) (Recurring transactions don't save (or show) selected subscription) reported by @SteffoSpieler +- [Discussion 10938](https://github.com/orgs/firefly-iii/discussions/10938) (Unable to apply default rule group to certain transactions) started by @praemon +- [Issue 10940](https://github.com/firefly-iii/firefly-iii/issues/10940) (Internal Server Error when trying to open piggy banks) reported by @mattephi +- [Issue 10954](https://github.com/firefly-iii/firefly-iii/issues/10954) (Internal Server Error when trying to access (default) account) reported by @thomaschristory +- [Issue 10956](https://github.com/firefly-iii/firefly-iii/issues/10956) (Manual webhook trigger fail) reported by @dudu7731 +- [Issue 10960](https://github.com/firefly-iii/firefly-iii/issues/10960) (404 after deleting subscription) reported by @lindely +- [Discussion 10974](https://github.com/orgs/firefly-iii/discussions/10974) (Big webhook_messages table) started by @Billos +- [Issue 10990](https://github.com/firefly-iii/firefly-iii/issues/10990) (duplicate piggy event via API) reported by @4e868df3 ### API -- #10803 -- #10908 +- [Issue 10803](https://github.com/firefly-iii/firefly-iii/issues/10803) (Issue in /v1/budget-limits spent attribute) reported by @Billos +- [Discussion 10908](https://github.com/orgs/firefly-iii/discussions/10908) (New fields for BudgetLimit object) started by @Billos ## 6.4.0 - 2025-09-14 diff --git a/config/firefly.php b/config/firefly.php index 24c618740a..01c88d918a 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -79,7 +79,7 @@ return [ // see cer.php for exchange rates feature flag. ], 'version' => 'develop/2025-10-02', - 'build_time' => 1759381368, + 'build_time' => 1759383469, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 28, // field is no longer used. From 3f81aa74036d6ab28692f4f0bd5ffd06847584ba Mon Sep 17 00:00:00 2001 From: Sander Dorigo Date: Thu, 2 Oct 2025 10:15:18 +0200 Subject: [PATCH 67/91] Fix #10988 --- app/Api/V1/Requests/Data/DateRequest.php | 10 ++++++++++ changelog.md | 1 + 2 files changed, 11 insertions(+) diff --git a/app/Api/V1/Requests/Data/DateRequest.php b/app/Api/V1/Requests/Data/DateRequest.php index 7ba1861086..05125a8868 100644 --- a/app/Api/V1/Requests/Data/DateRequest.php +++ b/app/Api/V1/Requests/Data/DateRequest.php @@ -46,6 +46,16 @@ class DateRequest extends FormRequest { $start = $this->getCarbonDate('start'); $end = $this->getCarbonDate('end'); + if(null === $start) { + $start = now()->startOfMonth(); + } + if(null === $end) { + $end = now()->endOfMonth(); + } + // 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/changelog.md b/changelog.md index a165d12d9b..1c9594c475 100644 --- a/changelog.md +++ b/changelog.md @@ -25,6 +25,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - [Issue 10956](https://github.com/firefly-iii/firefly-iii/issues/10956) (Manual webhook trigger fail) reported by @dudu7731 - [Issue 10960](https://github.com/firefly-iii/firefly-iii/issues/10960) (404 after deleting subscription) reported by @lindely - [Discussion 10974](https://github.com/orgs/firefly-iii/discussions/10974) (Big webhook_messages table) started by @Billos +- #10988 - [Issue 10990](https://github.com/firefly-iii/firefly-iii/issues/10990) (duplicate piggy event via API) reported by @4e868df3 ### API From 5088e20f258217bac1b6685c71139611271f3c9d Mon Sep 17 00:00:00 2001 From: Nicky De Maeyer Date: Thu, 2 Oct 2025 10:25:11 +0200 Subject: [PATCH 68/91] use correct translation key for category report income table --- resources/views/reports/category/partials/avg-income.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/reports/category/partials/avg-income.twig b/resources/views/reports/category/partials/avg-income.twig index b859f13a09..f473a8011b 100644 --- a/resources/views/reports/category/partials/avg-income.twig +++ b/resources/views/reports/category/partials/avg-income.twig @@ -2,7 +2,7 @@ {{ 'account'|_ }} - {{ 'spent_average'|_ }} + {{ 'income_average'|_ }} {{ 'total'|_ }} {{ 'transaction_count'|_ }} From a70fab1e878e10fcad24a8065081aaf042429161 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 2 Oct 2025 19:49:32 +0200 Subject: [PATCH 69/91] Clean up piggy alerts. --- app/Support/Http/Controllers/PeriodOverview.php | 8 ++------ app/TransactionRules/Actions/UpdatePiggyBank.php | 12 ++++++++++-- resources/lang/en_US/rules.php | 4 ++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index ddbe866e84..b639eff171 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -331,9 +331,7 @@ trait PeriodOverview } return $this->statistics->filter( - function (PeriodStatistic $statistic) use ($start, $end, $type) { - return $statistic->start->eq($start) && $statistic->end->eq($end) && $statistic->type === $type; - } + fn(PeriodStatistic $statistic) => $statistic->start->eq($start) && $statistic->end->eq($end) && $statistic->type === $type ); } @@ -346,9 +344,7 @@ trait PeriodOverview } return $this->statistics->filter( - function (PeriodStatistic $statistic) use ($start, $end, $prefix) { - return $statistic->start->eq($start) && $statistic->end->eq($end) && str_starts_with($statistic->type, $prefix); - } + fn(PeriodStatistic $statistic) => $statistic->start->eq($start) && $statistic->end->eq($end) && str_starts_with($statistic->type, $prefix) ); } diff --git a/app/TransactionRules/Actions/UpdatePiggyBank.php b/app/TransactionRules/Actions/UpdatePiggyBank.php index 64fe735e6a..e5bb2a9411 100644 --- a/app/TransactionRules/Actions/UpdatePiggyBank.php +++ b/app/TransactionRules/Actions/UpdatePiggyBank.php @@ -31,7 +31,9 @@ use FireflyIII\Models\PiggyBank; use FireflyIII\Models\RuleAction; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; +use FireflyIII\Support\Facades\Amount; use FireflyIII\User; use Illuminate\Support\Facades\Log; @@ -176,7 +178,9 @@ class UpdatePiggyBank implements ActionInterface private function removeAmount(PiggyBank $piggyBank, array $array, TransactionJournal $journal, Account $account, string $amount): void { $repository = app(PiggyBankRepositoryInterface::class); + $accountRepository = app(AccountRepositoryInterface::class); $repository->setUser($journal->user); + $accountRepository->setUser($account->user); // how much can we remove from this piggy bank? $toRemove = $repository->getCurrentAmount($piggyBank, $account); @@ -196,7 +200,8 @@ class UpdatePiggyBank implements ActionInterface 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]))); + $currency = $accountRepository->getAccountCurrency($account) ?? Amount::getPrimaryCurrency(); + event(new RuleActionFailedOnArray($this->action, $array, trans('rules.cannot_remove_from_piggy', ['amount' => Amount::formatAnything($amount, $currency, false), 'name' => $piggyBank->name]))); return; } @@ -208,7 +213,9 @@ class UpdatePiggyBank implements ActionInterface private function addAmount(PiggyBank $piggyBank, array $array, TransactionJournal $journal, Account $account, string $amount): void { $repository = app(PiggyBankRepositoryInterface::class); + $accountRepository = app(AccountRepositoryInterface::class); $repository->setUser($journal->user); + $accountRepository->setUser($account->user); // how much can we add to the piggy bank? if (0 !== bccomp($piggyBank->target_amount, '0')) { @@ -233,7 +240,8 @@ class UpdatePiggyBank implements ActionInterface 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]))); + $currency = $accountRepository->getAccountCurrency($account) ?? Amount::getPrimaryCurrency(); + event(new RuleActionFailedOnArray($this->action, $array, trans('rules.cannot_add_to_piggy', ['amount' => Amount::formatAnything($amount, $currency, false), 'name' => $piggyBank->name]))); return; } diff --git a/resources/lang/en_US/rules.php b/resources/lang/en_US/rules.php index ce60269015..405b13cb01 100644 --- a/resources/lang/en_US/rules.php +++ b/resources/lang/en_US/rules.php @@ -75,7 +75,7 @@ return [ '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_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"', + 'cannot_add_to_piggy' => 'Cannot add :amount to piggy bank ":name"', ]; From 354bbebbee3c0d8eb19633f708b5fcf112907370 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 2 Oct 2025 19:52:51 +0200 Subject: [PATCH 70/91] Fix #10965 --- resources/views/list/groups.twig | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/resources/views/list/groups.twig b/resources/views/list/groups.twig index a53f271fd8..fe4ab310d5 100644 --- a/resources/views/list/groups.twig +++ b/resources/views/list/groups.twig @@ -270,7 +270,12 @@ {% if (null == transaction.balance_dirty or false == transaction.balance_dirty) and null != transaction.destination_balance_after and null != transaction.source_balance_after %} {% if transaction.transaction_type_type == 'Deposit' %} - {{ formatAmountBySymbol(transaction.destination_balance_after, transaction.currency_symbol, transaction.currency_decimal_places) }} + {% if transaction.source_account_id == account.id %} + {{ formatAmountBySymbol(transaction.source_balance_after, transaction.currency_symbol, transaction.currency_decimal_places) }} + {% else %} + {{ formatAmountBySymbol(transaction.destination_balance_after, transaction.currency_symbol, transaction.currency_decimal_places) }} + {% endif %} + {% elseif transaction.transaction_type_type == 'Withdrawal' %} {% if 'Loan' == transaction.destination_account_type or 'Mortgage' == transaction.destination_account_type or 'Debt' == transaction.destination_account_type %} {% if currency.id == transaction.currency_id %} From e55af7186c261ac660bdf0b4d1a4bed19b5ea4e8 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 3 Oct 2025 05:24:24 +0200 Subject: [PATCH 71/91] Fix #10994 --- .../JsonApi/Enrichments/PiggyBankEnrichment.php | 15 +++++++++++---- changelog.md | 2 ++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php b/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php index 7c6a8a8a5b..fbc11552be 100644 --- a/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php +++ b/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php @@ -57,10 +57,12 @@ class PiggyBankEnrichment implements EnrichmentInterface private readonly TransactionCurrency $primaryCurrency; private User $user; private UserGroup $userGroup; + private ?Carbon $date; public function __construct() { $this->primaryCurrency = Amount::getPrimaryCurrency(); + $this->date = now(config('app.timezone')); } public function enrich(Collection $collection): Collection @@ -69,7 +71,6 @@ class PiggyBankEnrichment implements EnrichmentInterface $this->collectIds(); $this->collectObjectGroups(); $this->collectNotes(); - $this->collectCurrentAmounts(); $this->appendCollectedData(); @@ -157,8 +158,8 @@ class PiggyBankEnrichment implements EnrichmentInterface } // get suggested per month. - $meta['save_per_month'] = Steam::bcround($this->getSuggestedMonthlyAmount($item->start_date, $item->target_date, $meta['target_amount'], $meta['current_amount']), $currency->decimal_places); - $meta['pc_save_per_month'] = Steam::bcround($this->getSuggestedMonthlyAmount($item->start_date, $item->target_date, $meta['pc_target_amount'], $meta['pc_current_amount']), $currency->decimal_places); + $meta['save_per_month'] = Steam::bcround($this->getSuggestedMonthlyAmount($this->date, $item->target_date, $meta['target_amount'], $meta['current_amount']), $currency->decimal_places); + $meta['pc_save_per_month'] = Steam::bcround($this->getSuggestedMonthlyAmount($this->date, $item->target_date, $meta['pc_target_amount'], $meta['pc_current_amount']), $currency->decimal_places); $item->meta = $meta; @@ -166,7 +167,13 @@ class PiggyBankEnrichment implements EnrichmentInterface }); } - private function collectCurrentAmounts(): void {} + public function setDate(?Carbon $date): void + { + $this->date = $date; + } + + + private function collectIds(): void { diff --git a/changelog.md b/changelog.md index 1c9594c475..b0421f2815 100644 --- a/changelog.md +++ b/changelog.md @@ -24,9 +24,11 @@ This project adheres to [Semantic Versioning](http://semver.org/). - [Issue 10954](https://github.com/firefly-iii/firefly-iii/issues/10954) (Internal Server Error when trying to access (default) account) reported by @thomaschristory - [Issue 10956](https://github.com/firefly-iii/firefly-iii/issues/10956) (Manual webhook trigger fail) reported by @dudu7731 - [Issue 10960](https://github.com/firefly-iii/firefly-iii/issues/10960) (404 after deleting subscription) reported by @lindely +- #10965 - [Discussion 10974](https://github.com/orgs/firefly-iii/discussions/10974) (Big webhook_messages table) started by @Billos - #10988 - [Issue 10990](https://github.com/firefly-iii/firefly-iii/issues/10990) (duplicate piggy event via API) reported by @4e868df3 +- #10994 ### API From ca364dc8778b6bf0337fd59c20129db4b1f537cb Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 3 Oct 2025 05:53:50 +0200 Subject: [PATCH 72/91] Remove stats from empty objects. --- .../Events/StoredGroupEventHandler.php | 32 ++++++--- .../PeriodStatisticRepository.php | 70 ++++++++++--------- .../PeriodStatisticRepositoryInterface.php | 3 + .../Http/Controllers/PeriodOverview.php | 4 +- 4 files changed, 64 insertions(+), 45 deletions(-) diff --git a/app/Handlers/Events/StoredGroupEventHandler.php b/app/Handlers/Events/StoredGroupEventHandler.php index 50ce62f742..f01331a15c 100644 --- a/app/Handlers/Events/StoredGroupEventHandler.php +++ b/app/Handlers/Events/StoredGroupEventHandler.php @@ -62,14 +62,14 @@ class StoredGroupEventHandler } Log::debug('Now in StoredGroupEventHandler::processRules()'); - $journals = $storedGroupEvent->transactionGroup->transactionJournals; - $array = []; + $journals = $storedGroupEvent->transactionGroup->transactionJournals; + $array = []; /** @var TransactionJournal $journal */ foreach ($journals as $journal) { $array[] = $journal->id; } - $journalIds = implode(',', $array); + $journalIds = implode(',', $array); Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds)); // collect rules: @@ -78,10 +78,10 @@ class StoredGroupEventHandler // add the groups to the rule engine. // it should run the rules in the group and cancel the group if necessary. - $groups = $ruleGroupRepository->getRuleGroupsWithRules('store-journal'); + $groups = $ruleGroupRepository->getRuleGroupsWithRules('store-journal'); // create and fire rule engine. - $newRuleEngine = app(RuleEngineInterface::class); + $newRuleEngine = app(RuleEngineInterface::class); $newRuleEngine->setUser($storedGroupEvent->transactionGroup->user); $newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]); $newRuleEngine->setRuleGroups($groups); @@ -90,7 +90,7 @@ class StoredGroupEventHandler private function recalculateCredit(StoredTransactionGroup $event): void { - $group = $event->transactionGroup; + $group = $event->transactionGroup; /** @var CreditRecalculateService $object */ $object = app(CreditRecalculateService::class); @@ -109,12 +109,24 @@ class StoredGroupEventHandler $dest = $journal->transactions()->where('amount', '>', '0')->first(); $repository->deleteStatisticsForModel($source->account, $journal->date); $repository->deleteStatisticsForModel($dest->account, $journal->date); - foreach ($journal->categories as $category) { + $categories = $journal->categories; + $tags = $journal->tags; + $budgets = $journal->budgets; + foreach ($categories as $category) { $repository->deleteStatisticsForModel($category, $journal->date); } - foreach ($journal->tags as $tag) { + foreach ($tags as $tag) { $repository->deleteStatisticsForModel($tag, $journal->date); } + foreach ($budgets as $budget) { + $repository->deleteStatisticsForModel($budget, $journal->date); + } + if (0 === $categories->count()) { + $repository->deleteStatisticsForPrefix($journal->userGroup, 'no_category', $journal->date); + } + if (0 === $budgets->count()) { + $repository->deleteStatisticsForPrefix($journal->userGroup, 'no_budget', $journal->date); + } } } @@ -124,14 +136,14 @@ class StoredGroupEventHandler private function triggerWebhooks(StoredTransactionGroup $storedGroupEvent): void { Log::debug(__METHOD__); - $group = $storedGroupEvent->transactionGroup; + $group = $storedGroupEvent->transactionGroup; if (false === $storedGroupEvent->fireWebhooks) { Log::info(sprintf('Will not fire webhooks for transaction group #%d', $group->id)); return; } - $user = $group->user; + $user = $group->user; /** @var MessageGeneratorInterface $engine */ $engine = app(MessageGeneratorInterface::class); diff --git a/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php index ad294b9fcb..8763009535 100644 --- a/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php +++ b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php @@ -25,6 +25,7 @@ namespace FireflyIII\Repositories\PeriodStatistic; use Carbon\Carbon; use FireflyIII\Models\PeriodStatistic; +use FireflyIII\Models\UserGroup; use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use Illuminate\Database\Eloquent\Model; @@ -39,26 +40,24 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U public function findPeriodStatistics(Model $model, Carbon $start, Carbon $end, array $types): Collection { return $model->primaryPeriodStatistics() - ->where('start', $start) - ->where('end', $end) - ->whereIn('type', $types) - ->get() - ; + ->where('start', $start) + ->where('end', $end) + ->whereIn('type', $types) + ->get(); } public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection { return $model->primaryPeriodStatistics() - ->where('start', $start) - ->where('end', $end) - ->where('type', $type) - ->get() - ; + ->where('start', $start) + ->where('end', $end) + ->where('type', $type) + ->get(); } public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic { - $stat = new PeriodStatistic(); + $stat = new PeriodStatistic(); $stat->primaryStatable()->associate($model); $stat->transaction_currency_id = $currencyId; $stat->user_group_id = $this->getUserGroup()->id; @@ -72,16 +71,16 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U $stat->save(); Log::debug(sprintf( - 'Saved #%d [currency #%d, Model %s #%d, %s to %s, %d, %s] as new statistic.', - $stat->id, - $model::class, - $model->id, - $stat->transaction_currency_id, - $stat->start->toW3cString(), - $stat->end->toW3cString(), - $count, - $amount - )); + 'Saved #%d [currency #%d, Model %s #%d, %s to %s, %d, %s] as new statistic.', + $stat->id, + $model::class, + $model->id, + $stat->transaction_currency_id, + $stat->start->toW3cString(), + $stat->end->toW3cString(), + $count, + $amount + )); return $stat; } @@ -100,9 +99,8 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U public function allInRangeForPrefix(string $prefix, Carbon $start, Carbon $end): Collection { return $this->userGroup->periodStatistics() - ->where('type', 'LIKE', sprintf('%s%%', $prefix)) - ->where('start', '>=', $start)->where('end', '<=', $end)->get() - ; + ->where('type', 'LIKE', sprintf('%s%%', $prefix)) + ->where('start', '>=', $start)->where('end', '<=', $end)->get(); } #[Override] @@ -121,16 +119,22 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U $stat->save(); Log::debug(sprintf( - 'Saved #%d [currency #%d, type "%s", %s to %s, %d, %s] as new statistic.', - $stat->id, - $stat->transaction_currency_id, - $stat->type, - $stat->start->toW3cString(), - $stat->end->toW3cString(), - $count, - $amount - )); + 'Saved #%d [currency #%d, type "%s", %s to %s, %d, %s] as new statistic.', + $stat->id, + $stat->transaction_currency_id, + $stat->type, + $stat->start->toW3cString(), + $stat->end->toW3cString(), + $count, + $amount + )); return $stat; } + + #[\Override] + public function deleteStatisticsForPrefix(UserGroup $userGroup, string $prefix, Carbon $date): void + { + $userGroup->periodStatistics()->where('start', '<=', $date)->where('end', '>=', $date)->where('type', 'LIKE', sprintf('%s%%', $prefix))->delete(); + } } diff --git a/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php b/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php index 22f17d8f2d..c4a9574d29 100644 --- a/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php +++ b/app/Repositories/PeriodStatistic/PeriodStatisticRepositoryInterface.php @@ -25,6 +25,7 @@ namespace FireflyIII\Repositories\PeriodStatistic; use Carbon\Carbon; use FireflyIII\Models\PeriodStatistic; +use FireflyIII\Models\UserGroup; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; @@ -43,4 +44,6 @@ interface PeriodStatisticRepositoryInterface public function allInRangeForPrefix(string $prefix, Carbon $start, Carbon $end): Collection; public function deleteStatisticsForModel(Model $model, Carbon $date): void; + + public function deleteStatisticsForPrefix(UserGroup $userGroup, string $prefix, Carbon $date): void; } diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index b639eff171..1fc9dc3fd4 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -325,7 +325,7 @@ trait PeriodOverview private function filterStatistics(Carbon $start, Carbon $end, string $type): Collection { if (0 === $this->statistics->count()) { - Log::warning('Have no statistic to filter!'); + Log::debug('Have no statistic to filter!'); return new Collection(); } @@ -338,7 +338,7 @@ trait PeriodOverview private function filterPrefixedStatistics(Carbon $start, Carbon $end, string $prefix): Collection { if (0 === $this->statistics->count()) { - Log::warning('Have no statistic to filter!'); + Log::debug('Have no statistic to filter!'); return new Collection(); } From 037a128942339ef81cab576c6178ce6feeb16fc8 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 3 Oct 2025 05:57:21 +0200 Subject: [PATCH 73/91] Add missing translation. --- resources/lang/en_US/firefly.php | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 4a2ab3d092..f530ea76e0 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -909,6 +909,7 @@ return [ 'rule_trigger_tag_is' => 'Any tag is ":trigger_value"', 'rule_trigger_tag_contains_choice' => 'Any tag contains..', 'rule_trigger_tag_contains' => 'Any tag contains ":trigger_value"', + 'rule_trigger_not_tag_contains' => 'No tag contains ":trigger_value"', 'rule_trigger_tag_ends_choice' => 'Any tag ends with..', 'rule_trigger_tag_ends' => 'Any tag ends with ":trigger_value"', 'rule_trigger_tag_starts_choice' => 'Any tag starts with..', From 8e700944fd938c475892066bade687907f26d4e7 Mon Sep 17 00:00:00 2001 From: JC5 Date: Fri, 3 Oct 2025 06:04:16 +0200 Subject: [PATCH 74/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-10-03?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- THANKS.md | 1 + app/Api/V1/Requests/Data/DateRequest.php | 10 +-- .../Events/StoredGroupEventHandler.php | 20 +++--- .../PeriodStatisticRepository.php | 65 ++++++++++--------- .../Http/Controllers/PeriodOverview.php | 4 +- .../Enrichments/PiggyBankEnrichment.php | 5 +- .../Actions/UpdatePiggyBank.php | 8 +-- changelog.md | 6 +- composer.lock | 25 +++---- config/firefly.php | 4 +- package-lock.json | 42 ++++++------ resources/assets/v1/src/locales/ro.json | 2 +- 12 files changed, 94 insertions(+), 98 deletions(-) diff --git a/THANKS.md b/THANKS.md index 6c53dc2c9d..b9811e97f8 100755 --- a/THANKS.md +++ b/THANKS.md @@ -4,6 +4,7 @@ Over time, many people have contributed to Firefly III. Their efforts are not al Please find below all the people who contributed to the Firefly III code. Their names are mentioned in the year of their first contribution. ## 2025 +- Nicky De Maeyer - Denis Iskandarov - = - Lompi diff --git a/app/Api/V1/Requests/Data/DateRequest.php b/app/Api/V1/Requests/Data/DateRequest.php index 05125a8868..7155994862 100644 --- a/app/Api/V1/Requests/Data/DateRequest.php +++ b/app/Api/V1/Requests/Data/DateRequest.php @@ -44,16 +44,16 @@ class DateRequest extends FormRequest */ public function getAll(): array { - $start = $this->getCarbonDate('start'); - $end = $this->getCarbonDate('end'); - if(null === $start) { + $start = $this->getCarbonDate('start'); + $end = $this->getCarbonDate('end'); + if (null === $start) { $start = now()->startOfMonth(); } - if(null === $end) { + if (null === $end) { $end = now()->endOfMonth(); } // sanity check on dates: - [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; + [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; $start->startOfDay(); diff --git a/app/Handlers/Events/StoredGroupEventHandler.php b/app/Handlers/Events/StoredGroupEventHandler.php index f01331a15c..b960ff88df 100644 --- a/app/Handlers/Events/StoredGroupEventHandler.php +++ b/app/Handlers/Events/StoredGroupEventHandler.php @@ -62,14 +62,14 @@ class StoredGroupEventHandler } Log::debug('Now in StoredGroupEventHandler::processRules()'); - $journals = $storedGroupEvent->transactionGroup->transactionJournals; - $array = []; + $journals = $storedGroupEvent->transactionGroup->transactionJournals; + $array = []; /** @var TransactionJournal $journal */ foreach ($journals as $journal) { $array[] = $journal->id; } - $journalIds = implode(',', $array); + $journalIds = implode(',', $array); Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds)); // collect rules: @@ -78,10 +78,10 @@ class StoredGroupEventHandler // add the groups to the rule engine. // it should run the rules in the group and cancel the group if necessary. - $groups = $ruleGroupRepository->getRuleGroupsWithRules('store-journal'); + $groups = $ruleGroupRepository->getRuleGroupsWithRules('store-journal'); // create and fire rule engine. - $newRuleEngine = app(RuleEngineInterface::class); + $newRuleEngine = app(RuleEngineInterface::class); $newRuleEngine->setUser($storedGroupEvent->transactionGroup->user); $newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]); $newRuleEngine->setRuleGroups($groups); @@ -90,7 +90,7 @@ class StoredGroupEventHandler private function recalculateCredit(StoredTransactionGroup $event): void { - $group = $event->transactionGroup; + $group = $event->transactionGroup; /** @var CreditRecalculateService $object */ $object = app(CreditRecalculateService::class); @@ -105,8 +105,8 @@ class StoredGroupEventHandler /** @var TransactionJournal $journal */ foreach ($event->transactionGroup->transactionJournals as $journal) { - $source = $journal->transactions()->where('amount', '<', '0')->first(); - $dest = $journal->transactions()->where('amount', '>', '0')->first(); + $source = $journal->transactions()->where('amount', '<', '0')->first(); + $dest = $journal->transactions()->where('amount', '>', '0')->first(); $repository->deleteStatisticsForModel($source->account, $journal->date); $repository->deleteStatisticsForModel($dest->account, $journal->date); $categories = $journal->categories; @@ -136,14 +136,14 @@ class StoredGroupEventHandler private function triggerWebhooks(StoredTransactionGroup $storedGroupEvent): void { Log::debug(__METHOD__); - $group = $storedGroupEvent->transactionGroup; + $group = $storedGroupEvent->transactionGroup; if (false === $storedGroupEvent->fireWebhooks) { Log::info(sprintf('Will not fire webhooks for transaction group #%d', $group->id)); return; } - $user = $group->user; + $user = $group->user; /** @var MessageGeneratorInterface $engine */ $engine = app(MessageGeneratorInterface::class); diff --git a/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php index 8763009535..e2d6649064 100644 --- a/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php +++ b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php @@ -40,24 +40,26 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U public function findPeriodStatistics(Model $model, Carbon $start, Carbon $end, array $types): Collection { return $model->primaryPeriodStatistics() - ->where('start', $start) - ->where('end', $end) - ->whereIn('type', $types) - ->get(); + ->where('start', $start) + ->where('end', $end) + ->whereIn('type', $types) + ->get() + ; } public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection { return $model->primaryPeriodStatistics() - ->where('start', $start) - ->where('end', $end) - ->where('type', $type) - ->get(); + ->where('start', $start) + ->where('end', $end) + ->where('type', $type) + ->get() + ; } public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic { - $stat = new PeriodStatistic(); + $stat = new PeriodStatistic(); $stat->primaryStatable()->associate($model); $stat->transaction_currency_id = $currencyId; $stat->user_group_id = $this->getUserGroup()->id; @@ -71,16 +73,16 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U $stat->save(); Log::debug(sprintf( - 'Saved #%d [currency #%d, Model %s #%d, %s to %s, %d, %s] as new statistic.', - $stat->id, - $model::class, - $model->id, - $stat->transaction_currency_id, - $stat->start->toW3cString(), - $stat->end->toW3cString(), - $count, - $amount - )); + 'Saved #%d [currency #%d, Model %s #%d, %s to %s, %d, %s] as new statistic.', + $stat->id, + $model::class, + $model->id, + $stat->transaction_currency_id, + $stat->start->toW3cString(), + $stat->end->toW3cString(), + $count, + $amount + )); return $stat; } @@ -99,8 +101,9 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U public function allInRangeForPrefix(string $prefix, Carbon $start, Carbon $end): Collection { return $this->userGroup->periodStatistics() - ->where('type', 'LIKE', sprintf('%s%%', $prefix)) - ->where('start', '>=', $start)->where('end', '<=', $end)->get(); + ->where('type', 'LIKE', sprintf('%s%%', $prefix)) + ->where('start', '>=', $start)->where('end', '<=', $end)->get() + ; } #[Override] @@ -119,20 +122,20 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U $stat->save(); Log::debug(sprintf( - 'Saved #%d [currency #%d, type "%s", %s to %s, %d, %s] as new statistic.', - $stat->id, - $stat->transaction_currency_id, - $stat->type, - $stat->start->toW3cString(), - $stat->end->toW3cString(), - $count, - $amount - )); + 'Saved #%d [currency #%d, type "%s", %s to %s, %d, %s] as new statistic.', + $stat->id, + $stat->transaction_currency_id, + $stat->type, + $stat->start->toW3cString(), + $stat->end->toW3cString(), + $count, + $amount + )); return $stat; } - #[\Override] + #[Override] public function deleteStatisticsForPrefix(UserGroup $userGroup, string $prefix, Carbon $date): void { $userGroup->periodStatistics()->where('start', '<=', $date)->where('end', '>=', $date)->where('type', 'LIKE', sprintf('%s%%', $prefix))->delete(); diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index 1fc9dc3fd4..caa114007b 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -331,7 +331,7 @@ trait PeriodOverview } return $this->statistics->filter( - fn(PeriodStatistic $statistic) => $statistic->start->eq($start) && $statistic->end->eq($end) && $statistic->type === $type + fn (PeriodStatistic $statistic) => $statistic->start->eq($start) && $statistic->end->eq($end) && $statistic->type === $type ); } @@ -344,7 +344,7 @@ trait PeriodOverview } return $this->statistics->filter( - fn(PeriodStatistic $statistic) => $statistic->start->eq($start) && $statistic->end->eq($end) && str_starts_with($statistic->type, $prefix) + fn (PeriodStatistic $statistic) => $statistic->start->eq($start) && $statistic->end->eq($end) && str_starts_with($statistic->type, $prefix) ); } diff --git a/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php b/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php index fbc11552be..f1a79ad0fc 100644 --- a/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php +++ b/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php @@ -62,7 +62,7 @@ class PiggyBankEnrichment implements EnrichmentInterface public function __construct() { $this->primaryCurrency = Amount::getPrimaryCurrency(); - $this->date = now(config('app.timezone')); + $this->date = now(config('app.timezone')); } public function enrich(Collection $collection): Collection @@ -172,9 +172,6 @@ class PiggyBankEnrichment implements EnrichmentInterface $this->date = $date; } - - - private function collectIds(): void { /** @var PiggyBank $piggy */ diff --git a/app/TransactionRules/Actions/UpdatePiggyBank.php b/app/TransactionRules/Actions/UpdatePiggyBank.php index e5bb2a9411..9e21497d97 100644 --- a/app/TransactionRules/Actions/UpdatePiggyBank.php +++ b/app/TransactionRules/Actions/UpdatePiggyBank.php @@ -177,17 +177,17 @@ class UpdatePiggyBank implements ActionInterface private function removeAmount(PiggyBank $piggyBank, array $array, TransactionJournal $journal, Account $account, string $amount): void { - $repository = app(PiggyBankRepositoryInterface::class); + $repository = app(PiggyBankRepositoryInterface::class); $accountRepository = app(AccountRepositoryInterface::class); $repository->setUser($journal->user); $accountRepository->setUser($account->user); // how much can we remove from this piggy bank? - $toRemove = $repository->getCurrentAmount($piggyBank, $account); + $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; + $amount = -1 === bccomp($amount, $toRemove) ? $amount : $toRemove; Log::debug(sprintf('Amount is now %s', $amount)); // if amount is zero, stop. @@ -212,7 +212,7 @@ class UpdatePiggyBank implements ActionInterface private function addAmount(PiggyBank $piggyBank, array $array, TransactionJournal $journal, Account $account, string $amount): void { - $repository = app(PiggyBankRepositoryInterface::class); + $repository = app(PiggyBankRepositoryInterface::class); $accountRepository = app(AccountRepositoryInterface::class); $repository->setUser($journal->user); $accountRepository->setUser($account->user); diff --git a/changelog.md b/changelog.md index b0421f2815..588034223d 100644 --- a/changelog.md +++ b/changelog.md @@ -24,11 +24,11 @@ This project adheres to [Semantic Versioning](http://semver.org/). - [Issue 10954](https://github.com/firefly-iii/firefly-iii/issues/10954) (Internal Server Error when trying to access (default) account) reported by @thomaschristory - [Issue 10956](https://github.com/firefly-iii/firefly-iii/issues/10956) (Manual webhook trigger fail) reported by @dudu7731 - [Issue 10960](https://github.com/firefly-iii/firefly-iii/issues/10960) (404 after deleting subscription) reported by @lindely -- #10965 +- [Issue 10965](https://github.com/firefly-iii/firefly-iii/issues/10965) (Fix running balance on liability overview) reported by @JC5 - [Discussion 10974](https://github.com/orgs/firefly-iii/discussions/10974) (Big webhook_messages table) started by @Billos -- #10988 +- [Discussion 10988](https://github.com/orgs/firefly-iii/discussions/10988) (Call to a member function startOfDay() on null.) started by @molnarti - [Issue 10990](https://github.com/firefly-iii/firefly-iii/issues/10990) (duplicate piggy event via API) reported by @4e868df3 -- #10994 +- [Discussion 10994](https://github.com/orgs/firefly-iii/discussions/10994) (How does the save per month attribute from a piggy bank is calculated?) started by @AdriDevelopsThings ### API diff --git a/composer.lock b/composer.lock index bf27259208..c9b187ff48 100644 --- a/composer.lock +++ b/composer.lock @@ -6193,16 +6193,16 @@ }, { "name": "spatie/laravel-html", - "version": "3.12.0", + "version": "3.12.1", "source": { "type": "git", "url": "https://github.com/spatie/laravel-html.git", - "reference": "3655f335609d853f51e431698179ddfe05851126" + "reference": "72af3cad24d153c230ff6e1da9fa3b7b702834c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-html/zipball/3655f335609d853f51e431698179ddfe05851126", - "reference": "3655f335609d853f51e431698179ddfe05851126", + "url": "https://api.github.com/repos/spatie/laravel-html/zipball/72af3cad24d153c230ff6e1da9fa3b7b702834c9", + "reference": "72af3cad24d153c230ff6e1da9fa3b7b702834c9", "shasum": "" }, "require": { @@ -6259,7 +6259,7 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/laravel-html/tree/3.12.0" + "source": "https://github.com/spatie/laravel-html/tree/3.12.1" }, "funding": [ { @@ -6267,7 +6267,7 @@ "type": "custom" } ], - "time": "2025-03-21T08:58:06+00:00" + "time": "2025-10-02T07:26:38+00:00" }, { "name": "spatie/laravel-ignition", @@ -11333,16 +11333,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.29", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan-phar-composer-source.git", - "reference": "git" - }, + "version": "2.1.30", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d618573eed4a1b6b75e37b2e0b65ac65c885d88e", - "reference": "d618573eed4a1b6b75e37b2e0b65ac65c885d88e", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/a4a7f159927983dd4f7c8020ed227d80b7f39d7d", + "reference": "a4a7f159927983dd4f7c8020ed227d80b7f39d7d", "shasum": "" }, "require": { @@ -11387,7 +11382,7 @@ "type": "github" } ], - "time": "2025-09-25T06:58:18+00:00" + "time": "2025-10-02T16:07:52+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", diff --git a/config/firefly.php b/config/firefly.php index 01c88d918a..24a4a410e6 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -78,8 +78,8 @@ return [ 'running_balance_column' => env('USE_RUNNING_BALANCE', false), // see cer.php for exchange rates feature flag. ], - 'version' => 'develop/2025-10-02', - 'build_time' => 1759383469, + 'version' => 'develop/2025-10-03', + 'build_time' => 1759464146, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 28, // field is no longer used. diff --git a/package-lock.json b/package-lock.json index 77e6898b78..c97c70daf4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5726,9 +5726,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.228", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.228.tgz", - "integrity": "sha512-nxkiyuqAn4MJ1QbobwqJILiDtu/jk14hEAWaMiJmNPh1Z+jqoFlBFZjdXwLWGeVSeu9hGLg6+2G9yJaW8rBIFA==", + "version": "1.5.229", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.229.tgz", + "integrity": "sha512-cwhDcZKGcT/rEthLRJ9eBlMDkh1sorgsuk+6dpsehV0g9CABsIqBxU4rLRjG+d/U6pYU1s37A4lSKrVc5lSQYg==", "dev": true, "license": "ISC" }, @@ -10980,9 +10980,9 @@ } }, "node_modules/tapable": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", - "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "dev": true, "license": "MIT", "engines": { @@ -11085,9 +11085,9 @@ "license": "MIT" }, "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", - "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "license": "MIT", "dependencies": { @@ -11503,9 +11503,9 @@ } }, "node_modules/vite": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.7.tgz", - "integrity": "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==", + "version": "7.1.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.9.tgz", + "integrity": "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==", "dev": true, "license": "MIT", "dependencies": { @@ -11984,9 +11984,9 @@ "license": "MIT" }, "node_modules/webpack-dev-middleware/node_modules/schema-utils": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", - "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "license": "MIT", "dependencies": { @@ -12119,9 +12119,9 @@ } }, "node_modules/webpack-dev-server/node_modules/schema-utils": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", - "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "license": "MIT", "dependencies": { @@ -12221,9 +12221,9 @@ "license": "MIT" }, "node_modules/webpack/node_modules/schema-utils": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", - "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/resources/assets/v1/src/locales/ro.json b/resources/assets/v1/src/locales/ro.json index 118eaf015b..eec1bf47de 100644 --- a/resources/assets/v1/src/locales/ro.json +++ b/resources/assets/v1/src/locales/ro.json @@ -160,7 +160,7 @@ "url": "URL", "active": "Activ", "interest_date": "Data de interes", - "administration_currency": "Primary currency", + "administration_currency": "Moneda principala", "title": "Titlu", "date": "Dat\u0103", "book_date": "Rezerv\u0103 dat\u0103", From 957bc00847f2bfef3384de028033e7f22ac198d4 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 3 Oct 2025 06:41:42 +0200 Subject: [PATCH 75/91] Add statistics to budget, although unused atm. --- app/Models/Budget.php | 5 +++++ app/Models/Category.php | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/Models/Budget.php b/app/Models/Budget.php index 4375c6c2a7..481ad1c6dc 100644 --- a/app/Models/Budget.php +++ b/app/Models/Budget.php @@ -128,4 +128,9 @@ class Budget extends Model get: static fn ($value) => (int)$value, ); } + + public function primaryPeriodStatistics(): MorphMany + { + return $this->morphMany(PeriodStatistic::class, 'primary_statable'); + } } diff --git a/app/Models/Category.php b/app/Models/Category.php index 55c9c7ebcf..5eb63dbcba 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -81,7 +81,7 @@ class Category extends Model } /** - * Get all of the category's notes. + * Get all the category's notes. */ public function notes(): MorphMany { From 012172b0b5724a0b4e2107be24f7b540a28fc323 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 3 Oct 2025 06:42:24 +0200 Subject: [PATCH 76/91] Add statistics routine to update event. --- .../Events/UpdatedGroupEventHandler.php | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/app/Handlers/Events/UpdatedGroupEventHandler.php b/app/Handlers/Events/UpdatedGroupEventHandler.php index e1393a3355..28a687ea3d 100644 --- a/app/Handlers/Events/UpdatedGroupEventHandler.php +++ b/app/Handlers/Events/UpdatedGroupEventHandler.php @@ -72,12 +72,26 @@ class UpdatedGroupEventHandler $dest = $journal->transactions()->where('amount', '>', '0')->first(); $repository->deleteStatisticsForModel($source->account, $journal->date); $repository->deleteStatisticsForModel($dest->account, $journal->date); - foreach ($journal->categories as $category) { + + $categories = $journal->categories; + $tags = $journal->tags; + $budgets = $journal->budgets; + + foreach ($categories as $category) { $repository->deleteStatisticsForModel($category, $journal->date); } - foreach ($journal->tags as $tag) { + foreach ($tags as $tag) { $repository->deleteStatisticsForModel($tag, $journal->date); } + foreach ($budgets as $budget) { + $repository->deleteStatisticsForModel($budget, $journal->date); + } + if (0 === $categories->count()) { + $repository->deleteStatisticsForPrefix($journal->userGroup, 'no_category', $journal->date); + } + if (0 === $budgets->count()) { + $repository->deleteStatisticsForPrefix($journal->userGroup, 'no_budget', $journal->date); + } } } From 1652c3af72d0d3b3b994157c5daf0f62825a564b Mon Sep 17 00:00:00 2001 From: JC5 Date: Fri, 3 Oct 2025 06:46:56 +0200 Subject: [PATCH 77/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-10-03?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- THANKS.md | 1 - app/Handlers/Events/UpdatedGroupEventHandler.php | 4 ++-- composer.lock | 14 +++++++------- config/firefly.php | 2 +- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/THANKS.md b/THANKS.md index b9811e97f8..2839b6f7f6 100755 --- a/THANKS.md +++ b/THANKS.md @@ -6,7 +6,6 @@ Please find below all the people who contributed to the Firefly III code. Their ## 2025 - Nicky De Maeyer - Denis Iskandarov -- = - Lompi - Jose Diaz-Gonzalez - SoftBrix diff --git a/app/Handlers/Events/UpdatedGroupEventHandler.php b/app/Handlers/Events/UpdatedGroupEventHandler.php index 28a687ea3d..1fb89d2be6 100644 --- a/app/Handlers/Events/UpdatedGroupEventHandler.php +++ b/app/Handlers/Events/UpdatedGroupEventHandler.php @@ -68,8 +68,8 @@ class UpdatedGroupEventHandler /** @var TransactionJournal $journal */ foreach ($event->transactionGroup->transactionJournals as $journal) { - $source = $journal->transactions()->where('amount', '<', '0')->first(); - $dest = $journal->transactions()->where('amount', '>', '0')->first(); + $source = $journal->transactions()->where('amount', '<', '0')->first(); + $dest = $journal->transactions()->where('amount', '>', '0')->first(); $repository->deleteStatisticsForModel($source->account, $journal->date); $repository->deleteStatisticsForModel($dest->account, $journal->date); diff --git a/composer.lock b/composer.lock index c9b187ff48..982569fbbd 100644 --- a/composer.lock +++ b/composer.lock @@ -11815,16 +11815,16 @@ }, { "name": "phpunit/phpunit", - "version": "12.3.15", + "version": "12.4.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "b035ee2cd8ecad4091885b61017ebb1d80eb0e57" + "reference": "f62aab5794e36ccd26860db2d1bbf89ac19028d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b035ee2cd8ecad4091885b61017ebb1d80eb0e57", - "reference": "b035ee2cd8ecad4091885b61017ebb1d80eb0e57", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f62aab5794e36ccd26860db2d1bbf89ac19028d9", + "reference": "f62aab5794e36ccd26860db2d1bbf89ac19028d9", "shasum": "" }, "require": { @@ -11860,7 +11860,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "12.3-dev" + "dev-main": "12.4-dev" } }, "autoload": { @@ -11892,7 +11892,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.15" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.4.0" }, "funding": [ { @@ -11916,7 +11916,7 @@ "type": "tidelift" } ], - "time": "2025-09-28T12:10:54+00:00" + "time": "2025-10-03T04:28:03+00:00" }, { "name": "rector/rector", diff --git a/config/firefly.php b/config/firefly.php index 24a4a410e6..906dab11e0 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -79,7 +79,7 @@ return [ // see cer.php for exchange rates feature flag. ], 'version' => 'develop/2025-10-03', - 'build_time' => 1759464146, + 'build_time' => 1759466687, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 28, // field is no longer used. From 362705ec713ffe645547b17ceb9250dbce993f19 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 5 Oct 2025 07:47:36 +0200 Subject: [PATCH 78/91] Add missing entry. --- app/Support/Navigation.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php index cb9ac29d9f..5c9c61054b 100644 --- a/app/Support/Navigation.php +++ b/app/Support/Navigation.php @@ -423,6 +423,7 @@ class Navigation 'month' => (string)trans('config.month_js'), 'monthly' => (string)trans('config.month_js'), '1Y' => (string)trans('config.year_js'), + 'YTD' => (string)trans('config.year_js'), 'year' => (string)trans('config.year_js'), 'yearly' => (string)trans('config.year_js'), '6M' => (string)trans('config.half_year_js'), From 072212c1124047b80e00bc46607a1131aaecb927 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 5 Oct 2025 12:49:39 +0200 Subject: [PATCH 79/91] Replace log. --- .ci/phpstan.neon | 2 +- .../V1/Controllers/Chart/BudgetController.php | 53 ------------------- .../Controllers/Chart/CategoryController.php | 28 +++++----- .../CorrectsOpeningBalanceCurrencies.php | 3 +- .../Correction/CorrectsTransferBudgets.php | 5 +- .../Correction/CorrectsUnevenAmount.php | 4 +- .../Correction/RemovesEmptyJournals.php | 9 ++-- app/Console/Commands/Export/ExportsData.php | 5 +- .../Commands/System/ForcesDecimalSize.php | 3 +- .../Commands/System/VerifySecurityAlerts.php | 17 +++--- app/Console/Commands/Tools/ApplyRules.php | 2 +- app/Console/Commands/Tools/Cron.php | 24 ++++----- .../Upgrade/AddsTransactionIdentifiers.php | 3 +- .../Upgrade/RemovesDatabaseDecryption.php | 13 ++--- .../Commands/Upgrade/UpgradesAttachments.php | 3 +- .../Upgrade/UpgradesBudgetLimitPeriods.php | 5 +- .../Commands/Upgrade/UpgradesJournalNotes.php | 3 +- .../Upgrade/UpgradesLiabilitiesEight.php | 3 +- .../Commands/Upgrade/UpgradesToGroups.php | 37 ++++++------- .../Upgrade/UpgradesTransferCurrencies.php | 5 +- app/Console/Commands/VerifiesAccessToken.php | 9 ++-- app/Console/Kernel.php | 3 +- app/Events/DestroyedTransactionGroup.php | 3 +- app/Events/Model/PiggyBank/ChangedAmount.php | 3 +- .../Model/Rule/RuleActionFailedOnArray.php | 3 +- .../Model/Rule/RuleActionFailedOnObject.php | 3 +- app/Events/RequestedReportOnJournals.php | 3 +- .../Test/OwnerTestNotificationChannel.php | 3 +- .../Test/UserTestNotificationChannel.php | 3 +- app/Exceptions/GracefulNotFoundHandler.php | 19 +++---- app/Exceptions/Handler.php | 29 +++++----- app/Factory/AccountFactory.php | 30 ++++++----- app/Models/BudgetLimit.php | 21 +++++--- app/Models/Recurrence.php | 5 ++ app/Models/Rule.php | 3 ++ app/Models/RuleGroup.php | 3 ++ app/Models/TransactionGroup.php | 16 ++++-- app/Models/TransactionJournal.php | 1 + app/Support/Navigation.php | 2 - 39 files changed, 194 insertions(+), 195 deletions(-) diff --git a/.ci/phpstan.neon b/.ci/phpstan.neon index 1b8e889c72..d145b09f80 100644 --- a/.ci/phpstan.neon +++ b/.ci/phpstan.neon @@ -28,5 +28,5 @@ parameters: # The level 8 is the highest level. original was 5 # 7 is more than enough, higher just leaves NULL things. - level: 7 + level: 6 diff --git a/app/Api/V1/Controllers/Chart/BudgetController.php b/app/Api/V1/Controllers/Chart/BudgetController.php index 726f4c78bd..194bcf0362 100644 --- a/app/Api/V1/Controllers/Chart/BudgetController.php +++ b/app/Api/V1/Controllers/Chart/BudgetController.php @@ -259,59 +259,6 @@ class BudgetController extends Controller return $return; } - // /** - // * Function that processes each budget limit (per budget). - // * - // * If you have a budget limit in EUR, only transactions in EUR will be considered. - // * If you have a budget limit in GBP, only transactions in GBP will be considered. - // * - // * If you have a budget limit in EUR, and a transaction in GBP, it will not be considered for the EUR budget limit. - // * - // * @throws FireflyException - // */ - // private function budgetLimits(Budget $budget, Collection $limits): array - // { - // Log::debug(sprintf('Now in budgetLimits(#%d)', $budget->id)); - // $data = []; - // - // /** @var BudgetLimit $limit */ - // foreach ($limits as $limit) { - // $data = array_merge($data, $this->processLimit($budget, $limit)); - // } - // - // return $data; - // } - - // /** - // * @throws FireflyException - // */ - // private function processLimit(Budget $budget, BudgetLimit $limit): array - // { - // Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__)); - // $end = clone $limit->end_date; - // $end->endOfDay(); - // $spent = $this->opsRepository->listExpenses($limit->start_date, $end, null, new Collection()->push($budget)); - // $limitCurrencyId = $limit->transaction_currency_id; - // - // /** @var array $entry */ - // // only spent the entry where the entry's currency matches the budget limit's currency - // // so $filtered will only have 1 or 0 entries - // $filtered = array_filter($spent, fn ($entry) => $entry['currency_id'] === $limitCurrencyId); - // $result = $this->processExpenses($budget->id, $filtered, $limit->start_date, $end); - // if (1 === count($result)) { - // $compare = bccomp($limit->amount, (string)app('steam')->positive($result[$limitCurrencyId]['spent'])); - // $result[$limitCurrencyId]['budgeted'] = $limit->amount; - // if (1 === $compare) { - // // convert this amount into the primary currency: - // $result[$limitCurrencyId]['left'] = bcadd($limit->amount, (string)$result[$limitCurrencyId]['spent']); - // } - // if ($compare <= 0) { - // $result[$limitCurrencyId]['overspent'] = app('steam')->positive(bcadd($limit->amount, (string)$result[$limitCurrencyId]['spent'])); - // } - // } - // - // return $result; - // } private function filterLimit(int $currencyId, Collection $limits): ?BudgetLimit { diff --git a/app/Api/V1/Controllers/Chart/CategoryController.php b/app/Api/V1/Controllers/Chart/CategoryController.php index 9626c447a5..a81261e3ea 100644 --- a/app/Api/V1/Controllers/Chart/CategoryController.php +++ b/app/Api/V1/Controllers/Chart/CategoryController.php @@ -107,11 +107,11 @@ class CategoryController extends Controller $type = $journal['transaction_type_type']; $currency = $currencies[$journalCurrencyId] ?? $this->currencyRepos->find($journalCurrencyId); $currencies[$journalCurrencyId] = $currency; - $currencyId = (int)$currency->id; - $currencyName = (string)$currency->name; - $currencyCode = (string)$currency->code; - $currencySymbol = (string)$currency->symbol; - $currencyDecimalPlaces = (int)$currency->decimal_places; + $currencyId = $currency->id; + $currencyName = $currency->name; + $currencyCode = $currency->code; + $currencySymbol = $currency->symbol; + $currencyDecimalPlaces = $currency->decimal_places; $amount = Steam::positive((string)$journal['amount']); $pcAmount = null; @@ -120,11 +120,11 @@ class CategoryController extends Controller $pcAmount = $amount; } if ($this->convertToPrimary && $journalCurrencyId !== $this->primaryCurrency->id) { - $currencyId = (int)$this->primaryCurrency->id; - $currencyName = (string)$this->primaryCurrency->name; - $currencyCode = (string)$this->primaryCurrency->code; - $currencySymbol = (string)$this->primaryCurrency->symbol; - $currencyDecimalPlaces = (int)$this->primaryCurrency->decimal_places; + $currencyId = $this->primaryCurrency->id; + $currencyName = $this->primaryCurrency->name; + $currencyCode = $this->primaryCurrency->code; + $currencySymbol = $this->primaryCurrency->symbol; + $currencyDecimalPlaces = $this->primaryCurrency->decimal_places; $pcAmount = $converter->convert($currency, $this->primaryCurrency, $journal['date'], $amount); Log::debug(sprintf('Converted %s %s to %s %s', $journal['currency_code'], $amount, $this->primaryCurrency->code, $pcAmount)); } @@ -141,10 +141,10 @@ class CategoryController extends Controller 'currency_symbol' => $currencySymbol, 'currency_decimal_places' => $currencyDecimalPlaces, 'primary_currency_id' => (string)$this->primaryCurrency->id, - 'primary_currency_name' => (string)$this->primaryCurrency->name, - 'primary_currency_code' => (string)$this->primaryCurrency->code, - 'primary_currency_symbol' => (string)$this->primaryCurrency->symbol, - 'primary_currency_decimal_places' => (int)$this->primaryCurrency->decimal_places, + 'primary_currency_name' => $this->primaryCurrency->name, + 'primary_currency_code' => $this->primaryCurrency->code, + 'primary_currency_symbol' => $this->primaryCurrency->symbol, + 'primary_currency_decimal_places' => $this->primaryCurrency->decimal_places, 'period' => null, 'start_date' => $start->toAtomString(), 'end_date' => $end->toAtomString(), diff --git a/app/Console/Commands/Correction/CorrectsOpeningBalanceCurrencies.php b/app/Console/Commands/Correction/CorrectsOpeningBalanceCurrencies.php index 0383e72785..9857fa2209 100644 --- a/app/Console/Commands/Correction/CorrectsOpeningBalanceCurrencies.php +++ b/app/Console/Commands/Correction/CorrectsOpeningBalanceCurrencies.php @@ -34,6 +34,7 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use Illuminate\Console\Command; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Log; class CorrectsOpeningBalanceCurrencies extends Command { @@ -78,7 +79,7 @@ class CorrectsOpeningBalanceCurrencies extends Command $account = $this->getAccount($journal); if (!$account instanceof Account) { $message = sprintf('Transaction journal #%d has no valid account. Can\'t fix this line.', $journal->id); - app('log')->warning($message); + Log::warning($message); $this->friendlyError($message); return 0; diff --git a/app/Console/Commands/Correction/CorrectsTransferBudgets.php b/app/Console/Commands/Correction/CorrectsTransferBudgets.php index 5098087f76..a717741833 100644 --- a/app/Console/Commands/Correction/CorrectsTransferBudgets.php +++ b/app/Console/Commands/Correction/CorrectsTransferBudgets.php @@ -28,6 +28,7 @@ use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Models\TransactionJournal; use Illuminate\Console\Command; +use Illuminate\Support\Facades\Log; class CorrectsTransferBudgets extends Command { @@ -53,13 +54,13 @@ class CorrectsTransferBudgets extends Command foreach ($set as $entry) { $message = sprintf('Transaction journal #%d is a %s, so has no longer a budget.', $entry->id, $entry->transactionType->type); $this->friendlyInfo($message); - app('log')->debug($message); + Log::debug($message); $entry->budgets()->sync([]); ++$count; } if (0 !== $count) { $message = sprintf('Corrected %d invalid budget/journal entries (entry).', $count); - app('log')->debug($message); + Log::debug($message); $this->friendlyInfo($message); } diff --git a/app/Console/Commands/Correction/CorrectsUnevenAmount.php b/app/Console/Commands/Correction/CorrectsUnevenAmount.php index 4d1e527f3e..cc34405706 100644 --- a/app/Console/Commands/Correction/CorrectsUnevenAmount.php +++ b/app/Console/Commands/Correction/CorrectsUnevenAmount.php @@ -152,7 +152,7 @@ class CorrectsUnevenAmount extends Command $entry->the_sum ); $this->friendlyWarning($message); - app('log')->warning($message); + Log::warning($message); ++$this->count; continue; @@ -230,7 +230,7 @@ class CorrectsUnevenAmount extends Command $message = sprintf('Sum of journal #%d is not zero, journal is broken and now fixed.', $journal->id); $this->friendlyWarning($message); - app('log')->warning($message); + Log::warning($message); $destination->amount = $amount; $destination->save(); diff --git a/app/Console/Commands/Correction/RemovesEmptyJournals.php b/app/Console/Commands/Correction/RemovesEmptyJournals.php index 4ea323c72c..1e850b4bda 100644 --- a/app/Console/Commands/Correction/RemovesEmptyJournals.php +++ b/app/Console/Commands/Correction/RemovesEmptyJournals.php @@ -30,6 +30,7 @@ use FireflyIII\Models\TransactionJournal; use Illuminate\Console\Command; use Illuminate\Database\QueryException; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; class RemovesEmptyJournals extends Command { @@ -68,8 +69,8 @@ class RemovesEmptyJournals extends Command $journal = TransactionJournal::find($row->transaction_journal_id); $journal?->delete(); } catch (QueryException $e) { - app('log')->info(sprintf('Could not delete journal: %s', $e->getMessage())); - app('log')->error($e->getTraceAsString()); + Log::info(sprintf('Could not delete journal: %s', $e->getMessage())); + Log::error($e->getTraceAsString()); } Transaction::where('transaction_journal_id', $row->transaction_journal_id)->delete(); @@ -96,8 +97,8 @@ class RemovesEmptyJournals extends Command $journal = TransactionJournal::find($entry->id); $journal?->delete(); } catch (QueryException $e) { - app('log')->info(sprintf('Could not delete entry: %s', $e->getMessage())); - app('log')->error($e->getTraceAsString()); + Log::info(sprintf('Could not delete entry: %s', $e->getMessage())); + Log::error($e->getTraceAsString()); } $this->friendlyInfo(sprintf('Deleted empty transaction journal #%d', $entry->id)); diff --git a/app/Console/Commands/Export/ExportsData.php b/app/Console/Commands/Export/ExportsData.php index d1189a2b40..cce69d8e45 100644 --- a/app/Console/Commands/Export/ExportsData.php +++ b/app/Console/Commands/Export/ExportsData.php @@ -37,6 +37,7 @@ use FireflyIII\Support\Export\ExportDataGenerator; use Illuminate\Console\Command; use Illuminate\Support\Collection; use Exception; +use Illuminate\Support\Facades\Log; use InvalidArgumentException; use function Safe\file_put_contents; @@ -189,7 +190,7 @@ class ExportsData extends Command try { $date = Carbon::createFromFormat('!Y-m-d', $this->option($field)); } catch (InvalidArgumentException $e) { - app('log')->error($e->getMessage()); + Log::error($e->getMessage()); $this->friendlyError(sprintf('%s date "%s" must be formatted YYYY-MM-DD. Field will be ignored.', $field, $this->option('start'))); $error = true; } @@ -200,7 +201,7 @@ class ExportsData extends Command } } if (null === $this->option($field)) { - app('log')->info(sprintf('No date given in field "%s"', $field)); + Log::info(sprintf('No date given in field "%s"', $field)); $error = true; } diff --git a/app/Console/Commands/System/ForcesDecimalSize.php b/app/Console/Commands/System/ForcesDecimalSize.php index 91884011ce..b96ace66da 100644 --- a/app/Console/Commands/System/ForcesDecimalSize.php +++ b/app/Console/Commands/System/ForcesDecimalSize.php @@ -43,6 +43,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; use function Safe\mb_regex_encoding; use function Safe\json_encode; @@ -95,7 +96,7 @@ class ForcesDecimalSize extends Command */ public function handle(): int { - app('log')->debug('Now in ForceDecimalSize::handle()'); + Log::debug('Now in ForceDecimalSize::handle()'); $this->determineDatabaseType(); $this->friendlyError('Running this command is dangerous and can cause data loss.'); diff --git a/app/Console/Commands/System/VerifySecurityAlerts.php b/app/Console/Commands/System/VerifySecurityAlerts.php index 920142d794..75b7f85b10 100644 --- a/app/Console/Commands/System/VerifySecurityAlerts.php +++ b/app/Console/Commands/System/VerifySecurityAlerts.php @@ -27,6 +27,7 @@ namespace FireflyIII\Console\Commands\System; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use Illuminate\Console\Command; use Illuminate\Database\QueryException; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; use League\Flysystem\FilesystemException; @@ -54,7 +55,7 @@ class VerifySecurityAlerts extends Command $disk = Storage::disk('resources'); // Next line is ignored because it's a Laravel Facade. if (!$disk->has('alerts.json')) { // @phpstan-ignore-line - app('log')->debug('No alerts.json file present.'); + Log::debug('No alerts.json file present.'); return 0; } @@ -64,19 +65,19 @@ class VerifySecurityAlerts extends Command /** @var array $array */ foreach ($json as $array) { if ($version === $array['version'] && true === $array['advisory']) { - app('log')->debug(sprintf('Version %s has an alert!', $array['version'])); + Log::debug(sprintf('Version %s has an alert!', $array['version'])); // add advisory to configuration. $this->saveSecurityAdvisory($array); // depends on level if ('info' === $array['level']) { - app('log')->debug('INFO level alert'); + Log::debug('INFO level alert'); $this->friendlyInfo($array['message']); return 0; } if ('warning' === $array['level']) { - app('log')->debug('WARNING level alert'); + Log::debug('WARNING level alert'); $this->friendlyWarning('------------------------ :o'); $this->friendlyWarning($array['message']); $this->friendlyWarning('------------------------ :o'); @@ -84,7 +85,7 @@ class VerifySecurityAlerts extends Command return 0; } if ('danger' === $array['level']) { - app('log')->debug('DANGER level alert'); + Log::debug('DANGER level alert'); $this->friendlyError('------------------------ :-('); $this->friendlyError($array['message']); $this->friendlyError('------------------------ :-('); @@ -95,7 +96,7 @@ class VerifySecurityAlerts extends Command return 0; } } - app('log')->debug(sprintf('No security alerts for version %s', $version)); + Log::debug(sprintf('No security alerts for version %s', $version)); $this->friendlyPositive(sprintf('No security alerts for version %s', $version)); return 0; @@ -107,7 +108,7 @@ class VerifySecurityAlerts extends Command app('fireflyconfig')->delete('upgrade_security_message'); app('fireflyconfig')->delete('upgrade_security_level'); } catch (QueryException $e) { - app('log')->debug(sprintf('Could not delete old security advisory, but thats OK: %s', $e->getMessage())); + Log::debug(sprintf('Could not delete old security advisory, but thats OK: %s', $e->getMessage())); } } @@ -117,7 +118,7 @@ class VerifySecurityAlerts extends Command app('fireflyconfig')->set('upgrade_security_message', $array['message']); app('fireflyconfig')->set('upgrade_security_level', $array['level']); } catch (QueryException $e) { - app('log')->debug(sprintf('Could not save new security advisory, but thats OK: %s', $e->getMessage())); + Log::debug(sprintf('Could not save new security advisory, but thats OK: %s', $e->getMessage())); } } } diff --git a/app/Console/Commands/Tools/ApplyRules.php b/app/Console/Commands/Tools/ApplyRules.php index 6a42ac0990..0b07299ab1 100644 --- a/app/Console/Commands/Tools/ApplyRules.php +++ b/app/Console/Commands/Tools/ApplyRules.php @@ -315,7 +315,7 @@ class ApplyRules extends Command // if in rule selection, or group in selection or all rules, it's included. $test = $this->includeRule($rule, $group); if (true === $test) { - app('log')->debug(sprintf('Will include rule #%d "%s"', $rule->id, $rule->title)); + Log::debug(sprintf('Will include rule #%d "%s"', $rule->id, $rule->title)); $rulesToApply->push($rule); } } diff --git a/app/Console/Commands/Tools/Cron.php b/app/Console/Commands/Tools/Cron.php index 27a4e66a64..ec91c4ebde 100644 --- a/app/Console/Commands/Tools/Cron.php +++ b/app/Console/Commands/Tools/Cron.php @@ -76,8 +76,8 @@ class Cron extends Command try { $this->exchangeRatesCronJob($force, $date); } catch (FireflyException $e) { - app('log')->error($e->getMessage()); - app('log')->error($e->getTraceAsString()); + Log::error($e->getMessage()); + Log::error($e->getTraceAsString()); $this->friendlyError($e->getMessage()); } } @@ -87,8 +87,8 @@ class Cron extends Command try { $this->checkForUpdates($force); } catch (FireflyException $e) { - app('log')->error($e->getMessage()); - app('log')->error($e->getTraceAsString()); + Log::error($e->getMessage()); + Log::error($e->getTraceAsString()); $this->friendlyError($e->getMessage()); } } @@ -98,8 +98,8 @@ class Cron extends Command try { $this->recurringCronJob($force, $date); } catch (FireflyException $e) { - app('log')->error($e->getMessage()); - app('log')->error($e->getTraceAsString()); + Log::error($e->getMessage()); + Log::error($e->getTraceAsString()); $this->friendlyError($e->getMessage()); } } @@ -109,8 +109,8 @@ class Cron extends Command try { $this->autoBudgetCronJob($force, $date); } catch (FireflyException $e) { - app('log')->error($e->getMessage()); - app('log')->error($e->getTraceAsString()); + Log::error($e->getMessage()); + Log::error($e->getTraceAsString()); $this->friendlyError($e->getMessage()); } } @@ -120,8 +120,8 @@ class Cron extends Command try { $this->subscriptionWarningCronJob($force, $date); } catch (FireflyException $e) { - app('log')->error($e->getMessage()); - app('log')->error($e->getTraceAsString()); + Log::error($e->getMessage()); + Log::error($e->getTraceAsString()); $this->friendlyError($e->getMessage()); } } @@ -130,8 +130,8 @@ class Cron extends Command try { $this->webhookCronJob($force, $date); } catch (FireflyException $e) { - app('log')->error($e->getMessage()); - app('log')->error($e->getTraceAsString()); + Log::error($e->getMessage()); + Log::error($e->getTraceAsString()); $this->friendlyError($e->getMessage()); } } diff --git a/app/Console/Commands/Upgrade/AddsTransactionIdentifiers.php b/app/Console/Commands/Upgrade/AddsTransactionIdentifiers.php index af59ca61ce..ebcc347d0a 100644 --- a/app/Console/Commands/Upgrade/AddsTransactionIdentifiers.php +++ b/app/Console/Commands/Upgrade/AddsTransactionIdentifiers.php @@ -31,6 +31,7 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Journal\JournalCLIRepositoryInterface; use Illuminate\Console\Command; use Illuminate\Database\QueryException; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Schema; class AddsTransactionIdentifiers extends Command @@ -147,7 +148,7 @@ class AddsTransactionIdentifiers extends Command ->first() ; } catch (QueryException $e) { - app('log')->error($e->getMessage()); + Log::error($e->getMessage()); $this->friendlyError('Firefly III could not find the "identifier" field in the "transactions" table.'); $this->friendlyError(sprintf('This field is required for Firefly III version %s to run.', config('firefly.version'))); $this->friendlyError('Please run "php artisan migrate --force" to add this field to the table.'); diff --git a/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php b/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php index b1e3d198db..57463c5d8d 100644 --- a/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php +++ b/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php @@ -31,6 +31,7 @@ use Illuminate\Console\Command; use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Support\Facades\Crypt; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; use JsonException; use stdClass; @@ -96,7 +97,7 @@ class RemovesDatabaseDecryption extends Command try { $configVar = app('fireflyconfig')->get($configName, false); } catch (FireflyException $e) { - app('log')->error($e->getMessage()); + Log::error($e->getMessage()); } if (null !== $configVar) { return (bool) $configVar->data; @@ -129,8 +130,8 @@ class RemovesDatabaseDecryption extends Command } catch (FireflyException $e) { $message = sprintf('Could not decrypt field "%s" in row #%d of table "%s": %s', $field, $id, $table, $e->getMessage()); $this->friendlyError($message); - app('log')->error($message); - app('log')->error($e->getTraceAsString()); + Log::error($message); + Log::error($e->getTraceAsString()); } // A separate routine for preferences table: @@ -175,9 +176,9 @@ class RemovesDatabaseDecryption extends Command } catch (JsonException $e) { $message = sprintf('Could not JSON decode preference row #%d: %s. This does not have to be a problem.', $id, $e->getMessage()); $this->friendlyError($message); - app('log')->warning($message); - app('log')->warning($value); - app('log')->warning($e->getTraceAsString()); + Log::warning($message); + Log::warning($value); + Log::warning($e->getTraceAsString()); return; } diff --git a/app/Console/Commands/Upgrade/UpgradesAttachments.php b/app/Console/Commands/Upgrade/UpgradesAttachments.php index c3e0d32eb9..06f8175e08 100644 --- a/app/Console/Commands/Upgrade/UpgradesAttachments.php +++ b/app/Console/Commands/Upgrade/UpgradesAttachments.php @@ -29,6 +29,7 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Attachment; use FireflyIII\Models\Note; use Illuminate\Console\Command; +use Illuminate\Support\Facades\Log; class UpgradesAttachments extends Command { @@ -75,7 +76,7 @@ class UpgradesAttachments extends Command $att->description = ''; $att->save(); - app('log')->debug(sprintf('Migrated attachment #%s description to note #%d.', $att->id, $note->id)); + Log::debug(sprintf('Migrated attachment #%s description to note #%d.', $att->id, $note->id)); ++$count; } } diff --git a/app/Console/Commands/Upgrade/UpgradesBudgetLimitPeriods.php b/app/Console/Commands/Upgrade/UpgradesBudgetLimitPeriods.php index a43f05682d..d3581741b3 100644 --- a/app/Console/Commands/Upgrade/UpgradesBudgetLimitPeriods.php +++ b/app/Console/Commands/Upgrade/UpgradesBudgetLimitPeriods.php @@ -27,6 +27,7 @@ namespace FireflyIII\Console\Commands\Upgrade; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Models\BudgetLimit; use Illuminate\Console\Command; +use Illuminate\Support\Facades\Log; class UpgradesBudgetLimitPeriods extends Command { @@ -84,7 +85,7 @@ class UpgradesBudgetLimitPeriods extends Command $limit->end_date->format('Y-m-d') ); $this->friendlyWarning($message); - app('log')->warning($message); + Log::warning($message); return; } @@ -98,7 +99,7 @@ class UpgradesBudgetLimitPeriods extends Command $limit->end_date->format('Y-m-d'), $period ); - app('log')->debug($msg); + Log::debug($msg); } private function getLimitPeriod(BudgetLimit $limit): ?string diff --git a/app/Console/Commands/Upgrade/UpgradesJournalNotes.php b/app/Console/Commands/Upgrade/UpgradesJournalNotes.php index 788cf1f6c1..5a3390e3d6 100644 --- a/app/Console/Commands/Upgrade/UpgradesJournalNotes.php +++ b/app/Console/Commands/Upgrade/UpgradesJournalNotes.php @@ -28,6 +28,7 @@ use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Models\Note; use FireflyIII\Models\TransactionJournalMeta; use Illuminate\Console\Command; +use Illuminate\Support\Facades\Log; class UpgradesJournalNotes extends Command { @@ -66,7 +67,7 @@ class UpgradesJournalNotes extends Command $note->text = $meta->data; $note->save(); - app('log')->debug(sprintf('Migrated meta note #%d to Note #%d', $meta->id, $note->id)); + Log::debug(sprintf('Migrated meta note #%d to Note #%d', $meta->id, $note->id)); $meta->delete(); ++$count; diff --git a/app/Console/Commands/Upgrade/UpgradesLiabilitiesEight.php b/app/Console/Commands/Upgrade/UpgradesLiabilitiesEight.php index 9a2bf9f048..072c48ca08 100644 --- a/app/Console/Commands/Upgrade/UpgradesLiabilitiesEight.php +++ b/app/Console/Commands/Upgrade/UpgradesLiabilitiesEight.php @@ -35,6 +35,7 @@ use FireflyIII\Services\Internal\Destroy\TransactionGroupDestroyService; use FireflyIII\Services\Internal\Support\CreditRecalculateService; use FireflyIII\User; use Illuminate\Console\Command; +use Illuminate\Support\Facades\Log; class UpgradesLiabilitiesEight extends Command { @@ -186,7 +187,7 @@ class UpgradesLiabilitiesEight extends Command return; } - app('log')->warning('Did not find opening balance.'); + Log::warning('Did not find opening balance.'); } private function deleteTransactions(Account $account): int diff --git a/app/Console/Commands/Upgrade/UpgradesToGroups.php b/app/Console/Commands/Upgrade/UpgradesToGroups.php index 453c31e0d3..3ceee64711 100644 --- a/app/Console/Commands/Upgrade/UpgradesToGroups.php +++ b/app/Console/Commands/Upgrade/UpgradesToGroups.php @@ -38,6 +38,7 @@ use Illuminate\Console\Command; use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; use Exception; +use Illuminate\Support\Facades\Log; class UpgradesToGroups extends Command { @@ -127,11 +128,11 @@ class UpgradesToGroups extends Command { // double check transaction count. if ($journal->transactions->count() <= 2) { - app('log')->debug(sprintf('Will not try to convert journal #%d because it has 2 or fewer transactions.', $journal->id)); + Log::debug(sprintf('Will not try to convert journal #%d because it has 2 or fewer transactions.', $journal->id)); return; } - app('log')->debug(sprintf('Will now try to convert journal #%d', $journal->id)); + Log::debug(sprintf('Will now try to convert journal #%d', $journal->id)); $this->journalRepository->setUser($journal->user); $this->groupFactory->setUser($journal->user); @@ -144,15 +145,15 @@ class UpgradesToGroups extends Command ]; $destTransactions = $this->getDestinationTransactions($journal); - app('log')->debug(sprintf('Will use %d positive transactions to create a new group.', $destTransactions->count())); + Log::debug(sprintf('Will use %d positive transactions to create a new group.', $destTransactions->count())); /** @var Transaction $transaction */ foreach ($destTransactions as $transaction) { $data['transactions'][] = $this->generateTransaction($journal, $transaction); } - app('log')->debug(sprintf('Now calling transaction journal factory (%d transactions in array)', count($data['transactions']))); + Log::debug(sprintf('Now calling transaction journal factory (%d transactions in array)', count($data['transactions']))); $group = $this->groupFactory->create($data); - app('log')->debug('Done calling transaction journal factory'); + Log::debug('Done calling transaction journal factory'); // delete the old transaction journal. $this->service->destroy($journal); @@ -160,7 +161,7 @@ class UpgradesToGroups extends Command ++$this->count; // report on result: - app('log')->debug( + Log::debug( sprintf( 'Migrated journal #%d into group #%d with these journals: #%s', $journal->id, @@ -190,7 +191,7 @@ class UpgradesToGroups extends Command */ private function generateTransaction(TransactionJournal $journal, Transaction $transaction): array { - app('log')->debug(sprintf('Now going to add transaction #%d to the array.', $transaction->id)); + Log::debug(sprintf('Now going to add transaction #%d to the array.', $transaction->id)); $opposingTr = $this->findOpposingTransaction($journal, $transaction); if (!$opposingTr instanceof Transaction) { @@ -282,8 +283,8 @@ class UpgradesToGroups extends Command static function (Transaction $subject) use ($transaction) { $amount = (float) $transaction->amount * -1 === (float) $subject->amount; // intentional float $identifier = $transaction->identifier === $subject->identifier; - app('log')->debug(sprintf('Amount the same? %s', var_export($amount, true))); - app('log')->debug(sprintf('ID the same? %s', var_export($identifier, true))); + Log::debug(sprintf('Amount the same? %s', var_export($amount, true))); + Log::debug(sprintf('ID the same? %s', var_export($identifier, true))); return $amount && $identifier; } @@ -294,13 +295,13 @@ class UpgradesToGroups extends Command private function getTransactionBudget(Transaction $left, Transaction $right): ?int { - app('log')->debug('Now in getTransactionBudget()'); + Log::debug('Now in getTransactionBudget()'); // try to get a budget ID from the left transaction: /** @var null|Budget $budget */ $budget = $left->budgets()->first(); if (null !== $budget) { - app('log')->debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $left->id)); + Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $left->id)); return $budget->id; } @@ -309,11 +310,11 @@ class UpgradesToGroups extends Command /** @var null|Budget $budget */ $budget = $right->budgets()->first(); if (null !== $budget) { - app('log')->debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $right->id)); + Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $right->id)); return $budget->id; } - app('log')->debug('Neither left or right have a budget, return NULL'); + Log::debug('Neither left or right have a budget, return NULL'); // if all fails, return NULL. return null; @@ -321,13 +322,13 @@ class UpgradesToGroups extends Command private function getTransactionCategory(Transaction $left, Transaction $right): ?int { - app('log')->debug('Now in getTransactionCategory()'); + Log::debug('Now in getTransactionCategory()'); // try to get a category ID from the left transaction: /** @var null|Category $category */ $category = $left->categories()->first(); if (null !== $category) { - app('log')->debug(sprintf('Return category #%d, from transaction #%d', $category->id, $left->id)); + Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $left->id)); return $category->id; } @@ -336,11 +337,11 @@ class UpgradesToGroups extends Command /** @var null|Category $category */ $category = $right->categories()->first(); if (null !== $category) { - app('log')->debug(sprintf('Return category #%d, from transaction #%d', $category->id, $category->id)); + Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $category->id)); return $category->id; } - app('log')->debug('Neither left or right have a category, return NULL'); + Log::debug('Neither left or right have a category, return NULL'); // if all fails, return NULL. return null; @@ -354,7 +355,7 @@ class UpgradesToGroups extends Command $orphanedJournals = $this->cliRepository->getJournalsWithoutGroup(); $total = count($orphanedJournals); if ($total > 0) { - app('log')->debug(sprintf('Going to convert %d transaction journals. Please hold..', $total)); + Log::debug(sprintf('Going to convert %d transaction journals. Please hold..', $total)); $this->friendlyInfo(sprintf('Going to convert %d transaction journals. Please hold..', $total)); /** @var array $array */ diff --git a/app/Console/Commands/Upgrade/UpgradesTransferCurrencies.php b/app/Console/Commands/Upgrade/UpgradesTransferCurrencies.php index d92813d6cd..fee9fde03d 100644 --- a/app/Console/Commands/Upgrade/UpgradesTransferCurrencies.php +++ b/app/Console/Commands/Upgrade/UpgradesTransferCurrencies.php @@ -33,6 +33,7 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Journal\JournalCLIRepositoryInterface; use Illuminate\Console\Command; +use Illuminate\Support\Facades\Log; class UpgradesTransferCurrencies extends Command { @@ -262,7 +263,7 @@ class UpgradesTransferCurrencies extends Command // source account must have a currency preference. if (!$this->sourceCurrency instanceof TransactionCurrency) { $message = sprintf('Account #%d ("%s") must have currency preference but has none.', $this->sourceAccount->id, $this->sourceAccount->name); - app('log')->error($message); + Log::error($message); $this->friendlyError($message); return true; @@ -275,7 +276,7 @@ class UpgradesTransferCurrencies extends Command $this->destinationAccount->id, $this->destinationAccount->name ); - app('log')->error($message); + Log::error($message); $this->friendlyError($message); return true; diff --git a/app/Console/Commands/VerifiesAccessToken.php b/app/Console/Commands/VerifiesAccessToken.php index 9b9d3d06c2..b591978e4a 100644 --- a/app/Console/Commands/VerifiesAccessToken.php +++ b/app/Console/Commands/VerifiesAccessToken.php @@ -27,6 +27,7 @@ namespace FireflyIII\Console\Commands; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; +use Illuminate\Support\Facades\Log; /** * Trait VerifiesAccessToken. @@ -76,19 +77,19 @@ trait VerifiesAccessToken $user = $repository->find($userId); if (null === $user) { - app('log')->error(sprintf('verifyAccessToken(): no such user for input "%d"', $userId)); + Log::error(sprintf('verifyAccessToken(): no such user for input "%d"', $userId)); return false; } $accessToken = app('preferences')->getForUser($user, 'access_token'); if (null === $accessToken) { - app('log')->error(sprintf('User #%d has no access token, so cannot access command line options.', $userId)); + Log::error(sprintf('User #%d has no access token, so cannot access command line options.', $userId)); return false; } if ($accessToken->data !== $token) { - app('log')->error(sprintf('Invalid access token for user #%d.', $userId)); - app('log')->error(sprintf('Token given is "%s", expected something else.', $token)); + Log::error(sprintf('Invalid access token for user #%d.', $userId)); + Log::error(sprintf('Token given is "%s", expected something else.', $token)); return false; } diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index bccbb2c3e5..071cdb65ca 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -26,6 +26,7 @@ namespace FireflyIII\Console; use Illuminate\Console\Scheduling\Schedule; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; +use Illuminate\Support\Facades\Log; use Override; /** @@ -52,7 +53,7 @@ class Kernel extends ConsoleKernel { $schedule->call( static function (): void { - app('log')->error( + Log::error( 'Firefly III no longer users the Laravel scheduler to do cron jobs! Please read the instructions at https://docs.firefly-iii.org/' ); echo "\n"; diff --git a/app/Events/DestroyedTransactionGroup.php b/app/Events/DestroyedTransactionGroup.php index f6692981f9..80b2dbaf79 100644 --- a/app/Events/DestroyedTransactionGroup.php +++ b/app/Events/DestroyedTransactionGroup.php @@ -26,6 +26,7 @@ namespace FireflyIII\Events; use FireflyIII\Models\TransactionGroup; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\Log; /** * Class DestroyedTransactionGroup. @@ -39,6 +40,6 @@ class DestroyedTransactionGroup extends Event */ public function __construct(public TransactionGroup $transactionGroup) { - app('log')->debug(sprintf('Now in %s', __METHOD__)); + Log::debug(sprintf('Now in %s', __METHOD__)); } } diff --git a/app/Events/Model/PiggyBank/ChangedAmount.php b/app/Events/Model/PiggyBank/ChangedAmount.php index f60e8a5113..ffcae27f76 100644 --- a/app/Events/Model/PiggyBank/ChangedAmount.php +++ b/app/Events/Model/PiggyBank/ChangedAmount.php @@ -29,6 +29,7 @@ use FireflyIII\Models\PiggyBank; use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\Log; /** * Class ChangedAmount @@ -45,7 +46,7 @@ class ChangedAmount extends Event */ public function __construct(PiggyBank $piggyBank, string $amount, public ?TransactionJournal $transactionJournal, public ?TransactionGroup $transactionGroup) { - app('log')->debug(sprintf('Created piggy bank event for piggy bank #%d with amount %s', $piggyBank->id, $amount)); + Log::debug(sprintf('Created piggy bank event for piggy bank #%d with amount %s', $piggyBank->id, $amount)); $this->piggyBank = $piggyBank; $this->amount = $amount; } diff --git a/app/Events/Model/Rule/RuleActionFailedOnArray.php b/app/Events/Model/Rule/RuleActionFailedOnArray.php index fc537cb430..9cb80f7fe2 100644 --- a/app/Events/Model/Rule/RuleActionFailedOnArray.php +++ b/app/Events/Model/Rule/RuleActionFailedOnArray.php @@ -26,6 +26,7 @@ namespace FireflyIII\Events\Model\Rule; use FireflyIII\Models\RuleAction; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\Log; /** * Class RuleActionFailedOnArray @@ -36,6 +37,6 @@ class RuleActionFailedOnArray public function __construct(public RuleAction $ruleAction, public array $journal, public string $error) { - app('log')->debug('Created new RuleActionFailedOnArray'); + Log::debug('Created new RuleActionFailedOnArray'); } } diff --git a/app/Events/Model/Rule/RuleActionFailedOnObject.php b/app/Events/Model/Rule/RuleActionFailedOnObject.php index bde5cacdec..d774160f5b 100644 --- a/app/Events/Model/Rule/RuleActionFailedOnObject.php +++ b/app/Events/Model/Rule/RuleActionFailedOnObject.php @@ -27,6 +27,7 @@ namespace FireflyIII\Events\Model\Rule; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\Log; /** * Class RuleActionFailedOnObject @@ -37,6 +38,6 @@ class RuleActionFailedOnObject public function __construct(public RuleAction $ruleAction, public TransactionJournal $journal, public string $error) { - app('log')->debug('Created new RuleActionFailedOnObject'); + Log::debug('Created new RuleActionFailedOnObject'); } } diff --git a/app/Events/RequestedReportOnJournals.php b/app/Events/RequestedReportOnJournals.php index 8805776832..27df251eb3 100644 --- a/app/Events/RequestedReportOnJournals.php +++ b/app/Events/RequestedReportOnJournals.php @@ -29,6 +29,7 @@ use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Log; /** * Class RequestedReportOnJournals @@ -44,7 +45,7 @@ class RequestedReportOnJournals */ public function __construct(public int $userId, public Collection $groups) { - app('log')->debug('In event RequestedReportOnJournals.'); + Log::debug('In event RequestedReportOnJournals.'); } /** diff --git a/app/Events/Test/OwnerTestNotificationChannel.php b/app/Events/Test/OwnerTestNotificationChannel.php index efce1b2a80..759c0578fc 100644 --- a/app/Events/Test/OwnerTestNotificationChannel.php +++ b/app/Events/Test/OwnerTestNotificationChannel.php @@ -26,6 +26,7 @@ namespace FireflyIII\Events\Test; use FireflyIII\Notifications\Notifiables\OwnerNotifiable; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\Log; class OwnerTestNotificationChannel { @@ -38,7 +39,7 @@ class OwnerTestNotificationChannel */ public function __construct(string $channel, public OwnerNotifiable $owner) { - app('log')->debug(sprintf('Triggered OwnerTestNotificationChannel("%s")', $channel)); + Log::debug(sprintf('Triggered OwnerTestNotificationChannel("%s")', $channel)); $this->channel = $channel; } } diff --git a/app/Events/Test/UserTestNotificationChannel.php b/app/Events/Test/UserTestNotificationChannel.php index 4d028459d1..6ee93f0190 100644 --- a/app/Events/Test/UserTestNotificationChannel.php +++ b/app/Events/Test/UserTestNotificationChannel.php @@ -26,6 +26,7 @@ namespace FireflyIII\Events\Test; use FireflyIII\User; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\Log; class UserTestNotificationChannel { @@ -38,7 +39,7 @@ class UserTestNotificationChannel */ public function __construct(string $channel, public User $user) { - app('log')->debug(sprintf('Triggered UserTestNotificationChannel("%s")', $channel)); + Log::debug(sprintf('Triggered UserTestNotificationChannel("%s")', $channel)); $this->channel = $channel; } } diff --git a/app/Exceptions/GracefulNotFoundHandler.php b/app/Exceptions/GracefulNotFoundHandler.php index b2feef6ea6..762da63c55 100644 --- a/app/Exceptions/GracefulNotFoundHandler.php +++ b/app/Exceptions/GracefulNotFoundHandler.php @@ -33,6 +33,7 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\User; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Log; use Symfony\Component\HttpFoundation\Response; use Override; use Throwable; @@ -65,7 +66,7 @@ class GracefulNotFoundHandler extends ExceptionHandler switch ($name) { default: - app('log')->warning(sprintf('GracefulNotFoundHandler cannot handle route with name "%s"', $name)); + Log::warning(sprintf('GracefulNotFoundHandler cannot handle route with name "%s"', $name)); return parent::render($request, $e); @@ -152,7 +153,7 @@ class GracefulNotFoundHandler extends ExceptionHandler */ private function handleAccount(Request $request, Throwable $exception): Response { - app('log')->debug('404 page is probably a deleted account. Redirect to overview of account types.'); + Log::debug('404 page is probably a deleted account. Redirect to overview of account types.'); /** @var User $user */ $user = auth()->user(); @@ -169,7 +170,7 @@ class GracefulNotFoundHandler extends ExceptionHandler /** @var null|Account $account */ $account = $user->accounts()->withTrashed()->with(['accountType'])->find($accountId); if (null === $account) { - app('log')->error(sprintf('Could not find account %d, so give big fat error.', $accountId)); + Log::error(sprintf('Could not find account %d, so give big fat error.', $accountId)); return parent::render($request, $exception); } @@ -187,7 +188,7 @@ class GracefulNotFoundHandler extends ExceptionHandler */ private function handleGroup(Request $request, Throwable $exception) { - app('log')->debug('404 page is probably a deleted group. Redirect to overview of group types.'); + Log::debug('404 page is probably a deleted group. Redirect to overview of group types.'); /** @var User $user */ $user = auth()->user(); @@ -198,7 +199,7 @@ class GracefulNotFoundHandler extends ExceptionHandler /** @var null|TransactionGroup $group */ $group = $user->transactionGroups()->withTrashed()->find($groupId); if (null === $group) { - app('log')->error(sprintf('Could not find group %d, so give big fat error.', $groupId)); + Log::error(sprintf('Could not find group %d, so give big fat error.', $groupId)); return parent::render($request, $exception); } @@ -206,7 +207,7 @@ class GracefulNotFoundHandler extends ExceptionHandler /** @var null|TransactionJournal $journal */ $journal = $group->transactionJournals()->withTrashed()->first(); if (null === $journal) { - app('log')->error(sprintf('Could not find journal for group %d, so give big fat error.', $groupId)); + Log::error(sprintf('Could not find journal for group %d, so give big fat error.', $groupId)); return parent::render($request, $exception); } @@ -227,7 +228,7 @@ class GracefulNotFoundHandler extends ExceptionHandler */ private function handleAttachment(Request $request, Throwable $exception) { - app('log')->debug('404 page is probably a deleted attachment. Redirect to parent object.'); + Log::debug('404 page is probably a deleted attachment. Redirect to parent object.'); /** @var User $user */ $user = auth()->user(); @@ -238,7 +239,7 @@ class GracefulNotFoundHandler extends ExceptionHandler /** @var null|Attachment $attachment */ $attachment = $user->attachments()->withTrashed()->find($attachmentId); if (null === $attachment) { - app('log')->error(sprintf('Could not find attachment %d, so give big fat error.', $attachmentId)); + Log::error(sprintf('Could not find attachment %d, so give big fat error.', $attachmentId)); return parent::render($request, $exception); } @@ -260,7 +261,7 @@ class GracefulNotFoundHandler extends ExceptionHandler } } - app('log')->error(sprintf('Could not redirect attachment %d, its linked to a %s.', $attachmentId, $attachment->attachable_type)); + Log::error(sprintf('Could not redirect attachment %d, its linked to a %s.', $attachmentId, $attachment->attachable_type)); return parent::render($request, $exception); } diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index fbdde03e1c..3bde465581 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -36,6 +36,7 @@ use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Session\TokenMismatchException; use Illuminate\Support\Arr; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\ValidationException as LaravelValidationException; use Laravel\Passport\Exceptions\OAuthServerException as LaravelOAuthException; use League\OAuth2\Server\Exception\OAuthServerException; @@ -98,51 +99,51 @@ class Handler extends ExceptionHandler { $expectsJson = $request->expectsJson(); - app('log')->debug('Now in Handler::render()'); + Log::debug('Now in Handler::render()'); if ($e instanceof LaravelValidationException && $expectsJson) { // ignore it: controller will handle it. - app('log')->debug(sprintf('Return to parent to handle LaravelValidationException(%d)', $e->status)); + Log::debug(sprintf('Return to parent to handle LaravelValidationException(%d)', $e->status)); return parent::render($request, $e); } if ($e instanceof NotFoundHttpException && $expectsJson) { // JSON error: - app('log')->debug('Return JSON not found error.'); + Log::debug('Return JSON not found error.'); return response()->json(['message' => 'Resource not found', 'exception' => 'NotFoundHttpException'], 404); } if ($e instanceof AuthorizationException && $expectsJson) { // somehow Laravel handler does not catch this: - app('log')->debug('Return JSON unauthorized error.'); + Log::debug('Return JSON unauthorized error.'); return response()->json(['message' => $e->getMessage(), 'exception' => 'AuthorizationException'], 401); } if ($e instanceof AuthenticationException && $expectsJson) { // somehow Laravel handler does not catch this: - app('log')->debug('Return JSON unauthenticated error.'); + Log::debug('Return JSON unauthenticated error.'); return response()->json(['message' => $e->getMessage(), 'exception' => 'AuthenticationException'], 401); } if ($e instanceof OAuthServerException && $expectsJson) { - app('log')->debug('Return JSON OAuthServerException.'); + Log::debug('Return JSON OAuthServerException.'); // somehow Laravel handler does not catch this: return response()->json(['message' => $e->getMessage(), 'exception' => 'OAuthServerException'], 401); } if ($e instanceof BadRequestHttpException) { - app('log')->debug('Return JSON BadRequestHttpException.'); + Log::debug('Return JSON BadRequestHttpException.'); return response()->json(['message' => $e->getMessage(), 'exception' => 'HttpException'], 400); } if ($e instanceof BadHttpHeaderException) { // is always API exception. - app('log')->debug('Return JSON BadHttpHeaderException.'); + Log::debug('Return JSON BadHttpHeaderException.'); return response()->json(['message' => $e->getMessage(), 'exception' => 'BadHttpHeaderException'], $e->statusCode); } @@ -161,7 +162,7 @@ class Handler extends ExceptionHandler $isDebug = (bool) config('app.debug', false); if ($isDebug) { - app('log')->debug(sprintf('Return JSON %s with debug.', $e::class)); + Log::debug(sprintf('Return JSON %s with debug.', $e::class)); return response()->json( [ @@ -174,7 +175,7 @@ class Handler extends ExceptionHandler $errorCode ); } - app('log')->debug(sprintf('Return JSON %s.', $e::class)); + Log::debug(sprintf('Return JSON %s.', $e::class)); return response()->json( ['message' => sprintf('Internal Firefly III Exception: %s', $e->getMessage()), 'exception' => 'UndisclosedException'], @@ -183,7 +184,7 @@ class Handler extends ExceptionHandler } if ($e instanceof NotFoundHttpException) { - app('log')->debug('Refer to GracefulNotFoundHandler'); + Log::debug('Refer to GracefulNotFoundHandler'); $handler = app(GracefulNotFoundHandler::class); return $handler->render($request, $e); @@ -191,20 +192,20 @@ class Handler extends ExceptionHandler // special view for database errors with extra instructions if ($e instanceof QueryException) { - app('log')->debug('Return Firefly III database exception view.'); + Log::debug('Return Firefly III database exception view.'); $isDebug = config('app.debug'); return response()->view('errors.DatabaseException', ['exception' => $e, 'debug' => $isDebug], 500); } if ($e instanceof FireflyException || $e instanceof ErrorException || $e instanceof OAuthServerException) { - app('log')->debug('Return Firefly III error view.'); + Log::debug('Return Firefly III error view.'); $isDebug = config('app.debug'); return response()->view('errors.FireflyException', ['exception' => $e, 'debug' => $isDebug], 500); } - app('log')->debug(sprintf('Error "%s" has no Firefly III treatment, parent will handle.', $e::class)); + Log::debug(sprintf('Error "%s" has no Firefly III treatment, parent will handle.', $e::class)); return parent::render($request, $e); } diff --git a/app/Factory/AccountFactory.php b/app/Factory/AccountFactory.php index 94e9f5a5d9..3ea9d77a49 100644 --- a/app/Factory/AccountFactory.php +++ b/app/Factory/AccountFactory.php @@ -72,16 +72,18 @@ class AccountFactory */ public function findOrCreate(string $accountName, string $accountType): Account { - app('log')->debug(sprintf('findOrCreate("%s", "%s")', $accountName, $accountType)); + Log::debug(sprintf('findOrCreate("%s", "%s")', $accountName, $accountType)); $type = $this->accountRepository->getAccountTypeByType($accountType); if (!$type instanceof AccountType) { throw new FireflyException(sprintf('Cannot find account type "%s"', $accountType)); } + /** @var Account|null $return */ $return = $this->user->accounts->where('account_type_id', $type->id)->where('name', $accountName)->first(); if (null === $return) { - app('log')->debug('Found nothing. Will create a new one.'); + Log::debug('Found nothing. Will create a new one.'); + /** @var Account $return */ $return = $this->create( [ 'user_id' => $this->user->id, @@ -104,7 +106,7 @@ class AccountFactory */ public function create(array $data): Account { - app('log')->debug('Now in AccountFactory::create()'); + Log::debug('Now in AccountFactory::create()'); $type = $this->getAccountType($data); $data['iban'] = $this->filterIban($data['iban'] ?? null); @@ -146,18 +148,18 @@ class AccountFactory } } if (null === $result) { - app('log')->warning(sprintf('Found NO account type based on %d and "%s"', $accountTypeId, $accountTypeName)); + Log::warning(sprintf('Found NO account type based on %d and "%s"', $accountTypeId, $accountTypeName)); throw new FireflyException(sprintf('AccountFactory::create() was unable to find account type #%d ("%s").', $accountTypeId, $accountTypeName)); } - app('log')->debug(sprintf('Found account type based on %d and "%s": "%s"', $accountTypeId, $accountTypeName, $result->type)); + Log::debug(sprintf('Found account type based on %d and "%s": "%s"', $accountTypeId, $accountTypeName, $result->type)); return $result; } public function find(string $accountName, string $accountType): ?Account { - app('log')->debug(sprintf('Now in AccountFactory::find("%s", "%s")', $accountName, $accountType)); + Log::debug(sprintf('Now in AccountFactory::find("%s", "%s")', $accountName, $accountType)); $type = AccountType::whereType($accountType)->first(); /** @var null|Account */ @@ -204,16 +206,16 @@ class AccountFactory try { $this->storeOpeningBalance($account, $data); } catch (FireflyException $e) { - app('log')->error($e->getMessage()); - app('log')->error($e->getTraceAsString()); + Log::error($e->getMessage()); + Log::error($e->getTraceAsString()); } // create credit liability data (only liabilities) try { $this->storeCreditLiability($account, $data); } catch (FireflyException $e) { - app('log')->error($e->getMessage()); - app('log')->error($e->getTraceAsString()); + Log::error($e->getMessage()); + Log::error($e->getTraceAsString()); } // create notes @@ -319,22 +321,22 @@ class AccountFactory */ private function storeCreditLiability(Account $account, array $data): void { - app('log')->debug('storeCreditLiability'); + Log::debug('storeCreditLiability'); $account->refresh(); $accountType = $account->accountType->type; $direction = $this->accountRepository->getMetaValue($account, 'liability_direction'); $valid = config('firefly.valid_liabilities'); if (in_array($accountType, $valid, true)) { - app('log')->debug('Is a liability with credit ("i am owed") direction.'); + Log::debug('Is a liability with credit ("i am owed") direction.'); if ($this->validOBData($data)) { - app('log')->debug('Has valid CL data.'); + Log::debug('Has valid CL data.'); $openingBalance = $data['opening_balance']; $openingBalanceDate = $data['opening_balance_date']; // store credit transaction. $this->updateCreditTransaction($account, $direction, $openingBalance, $openingBalanceDate); } if (!$this->validOBData($data)) { - app('log')->debug('Does NOT have valid CL data, deletr any CL transaction.'); + Log::debug('Does NOT have valid CL data, deletr any CL transaction.'); $this->deleteCreditTransaction($account); } } diff --git a/app/Models/BudgetLimit.php b/app/Models/BudgetLimit.php index 66f12753c6..f29c7102d1 100644 --- a/app/Models/BudgetLimit.php +++ b/app/Models/BudgetLimit.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use FireflyIII\Casts\SeparateTimezoneCaster; use FireflyIII\Handlers\Observer\BudgetLimitObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; @@ -33,6 +34,13 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphMany; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +/** + * Class BudgetLimit + * + * @property TransactionCurrency $transactionCurrency + * @property Carbon $start_date + * @property Carbon $end_date + */ #[ObservedBy([BudgetLimitObserver::class])] class BudgetLimit extends Model { @@ -50,10 +58,9 @@ class BudgetLimit extends Model if (auth()->check()) { $budgetLimitId = (int)$value; $budgetLimit = self::where('budget_limits.id', $budgetLimitId) - ->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') - ->where('budgets.user_id', auth()->user()->id) - ->first(['budget_limits.*']) - ; + ->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') + ->where('budgets.user_id', auth()->user()->id) + ->first(['budget_limits.*']); if (null !== $budgetLimit) { return $budgetLimit; } @@ -86,14 +93,14 @@ class BudgetLimit extends Model protected function amount(): Attribute { return Attribute::make( - get: static fn ($value) => (string)$value, + get: static fn($value) => (string)$value, ); } protected function budgetId(): Attribute { return Attribute::make( - get: static fn ($value) => (int)$value, + get: static fn($value) => (int)$value, ); } @@ -113,7 +120,7 @@ class BudgetLimit extends Model protected function transactionCurrencyId(): Attribute { return Attribute::make( - get: static fn ($value) => (int)$value, + get: static fn($value) => (int)$value, ); } } diff --git a/app/Models/Recurrence.php b/app/Models/Recurrence.php index b3fa7a3a8c..306be4dde4 100644 --- a/app/Models/Recurrence.php +++ b/app/Models/Recurrence.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use FireflyIII\Casts\SeparateTimezoneCaster; use FireflyIII\Handlers\Observer\RecurrenceObserver; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; @@ -38,6 +39,10 @@ use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +/** + * @property Carbon $first_date + * @property Carbon|null $latest_date + */ #[ObservedBy([RecurrenceObserver::class])] class Recurrence extends Model { diff --git a/app/Models/Rule.php b/app/Models/Rule.php index 7755e00984..d2c235f532 100644 --- a/app/Models/Rule.php +++ b/app/Models/Rule.php @@ -35,6 +35,9 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +/** + * @property User $user + */ #[ObservedBy([RuleObserver::class])] class Rule extends Model { diff --git a/app/Models/RuleGroup.php b/app/Models/RuleGroup.php index 99f6655f63..514d4d222e 100644 --- a/app/Models/RuleGroup.php +++ b/app/Models/RuleGroup.php @@ -35,6 +35,9 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +/** + * @property User $user + */ #[ObservedBy([RuleGroupObserver::class])] class RuleGroup extends Model { diff --git a/app/Models/TransactionGroup.php b/app/Models/TransactionGroup.php index 81e7aac2e6..544cdd3830 100644 --- a/app/Models/TransactionGroup.php +++ b/app/Models/TransactionGroup.php @@ -32,8 +32,16 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Log; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; + +/** + * @property User $user + * @property UserGroup $userGroup + * @property Collection $transactionJournals + */ #[ObservedBy([TransactionGroupObserver::class])] class TransactionGroup extends Model { @@ -50,13 +58,13 @@ class TransactionGroup extends Model */ public static function routeBinder(string $value): self { - app('log')->debug(sprintf('Now in %s("%s")', __METHOD__, $value)); + Log::debug(sprintf('Now in %s("%s")', __METHOD__, $value)); if (auth()->check()) { $groupId = (int)$value; /** @var User $user */ $user = auth()->user(); - app('log')->debug(sprintf('User authenticated as %s', $user->email)); + Log::debug(sprintf('User authenticated as %s', $user->email)); /** @var null|TransactionGroup $group */ $group = $user->transactionGroups() @@ -64,12 +72,12 @@ class TransactionGroup extends Model ->where('transaction_groups.id', $groupId)->first(['transaction_groups.*']) ; if (null !== $group) { - app('log')->debug(sprintf('Found group #%d.', $group->id)); + Log::debug(sprintf('Found group #%d.', $group->id)); return $group; } } - app('log')->debug('Found no group.'); + Log::debug('Found no group.'); throw new NotFoundHttpException(); } diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index 3aa0099446..72e1cd35cc 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -47,6 +47,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method EloquentBuilder|static before() * @method EloquentBuilder|static after() * @method static EloquentBuilder|static query() + * @property TransactionGroup $transactionGroup */ #[ObservedBy([TransactionJournalObserver::class])] class TransactionJournal extends Model diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php index 5c9c61054b..534eff6fe6 100644 --- a/app/Support/Navigation.php +++ b/app/Support/Navigation.php @@ -442,8 +442,6 @@ class Navigation Log::error(sprintf('No date formats for frequency "%s"!', $repeatFrequency)); throw new FireflyException(sprintf('No date formats for frequency "%s"!', $repeatFrequency)); - - return $date->format('Y-m-d'); } /** From 6341743cf91ab9085fb6c5cf738ff045e2ce871d Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 5 Oct 2025 12:57:58 +0200 Subject: [PATCH 80/91] Various code cleanup. --- .../Data/Export/ExportController.php | 44 +++++++++++++++---- .../Models/Account/ListController.php | 3 -- .../Models/Account/ShowController.php | 1 - .../Models/Attachment/ShowController.php | 1 - .../Models/AvailableBudget/ShowController.php | 1 - .../Models/Bill/ListController.php | 3 -- .../Models/Bill/ShowController.php | 1 - .../Models/Budget/ListController.php | 4 -- .../Models/Budget/ShowController.php | 1 - .../Models/BudgetLimit/ListController.php | 1 - .../Models/Category/ListController.php | 2 - .../Models/Category/ShowController.php | 1 - .../Models/ObjectGroup/ListController.php | 2 - .../Models/PiggyBank/ListController.php | 3 -- .../Models/PiggyBank/ShowController.php | 1 - .../Models/Recurrence/ListController.php | 1 - .../Models/Recurrence/ShowController.php | 1 - .../Models/Recurrence/TriggerController.php | 4 +- .../Models/Rule/ShowController.php | 1 - .../Models/RuleGroup/ListController.php | 1 - .../Models/RuleGroup/ShowController.php | 1 - .../Controllers/Models/Tag/ListController.php | 2 - .../Controllers/Models/Tag/ShowController.php | 1 - .../Models/Transaction/ListController.php | 3 -- .../Models/Transaction/ShowController.php | 1 - .../TransactionCurrency/ListController.php | 7 --- .../TransactionCurrency/ShowController.php | 3 -- .../TransactionCurrency/StoreController.php | 1 - .../TransactionCurrency/UpdateController.php | 2 - .../Models/TransactionLink/ShowController.php | 1 - .../TransactionLinkType/ListController.php | 1 - .../TransactionLinkType/ShowController.php | 1 - .../Search/TransactionController.php | 1 - .../V1/Controllers/System/UserController.php | 1 - .../User/PreferencesController.php | 1 - .../Controllers/Webhook/MessageController.php | 1 - .../V1/Controllers/Webhook/ShowController.php | 1 - .../Correction/ConvertsDatesToUTC.php | 1 - .../RemovesOrphanedTransactions.php | 4 +- app/Console/Commands/Export/ExportsData.php | 5 +++ .../System/CallsLaravelPassportKeys.php | 2 +- .../Commands/System/ForcesDecimalSize.php | 5 +-- .../Commands/System/VerifySecurityAlerts.php | 3 ++ .../Upgrade/AddsTransactionIdentifiers.php | 6 +-- .../Upgrade/RemovesDatabaseDecryption.php | 5 +-- .../Upgrade/UpgradesAccountCurrencies.php | 1 - .../Upgrade/UpgradesAccountMetaData.php | 5 +-- .../Commands/Upgrade/UpgradesAttachments.php | 5 +-- .../Commands/Upgrade/UpgradesBillsToRules.php | 6 +-- .../Commands/Upgrade/UpgradesBudgetLimits.php | 5 +-- .../Upgrade/UpgradesCurrencyPreferences.php | 5 +-- .../Commands/Upgrade/UpgradesJournalNotes.php | 5 +-- .../Commands/Upgrade/UpgradesLiabilities.php | 5 +-- .../Upgrade/UpgradesLiabilitiesEight.php | 5 +-- .../Upgrade/UpgradesMultiPiggyBanks.php | 5 +-- .../UpgradesPrimaryCurrencyAmounts.php | 5 +-- .../Upgrade/UpgradesRecurrenceMetaData.php | 5 +-- .../Commands/Upgrade/UpgradesRuleActions.php | 5 +-- .../Commands/Upgrade/UpgradesTagLocations.php | 5 +-- .../Commands/Upgrade/UpgradesToGroups.php | 5 +-- .../Upgrade/UpgradesTransferCurrencies.php | 5 +-- .../UpgradesVariousCurrencyInformation.php | 5 +-- .../Commands/Upgrade/UpgradesWebhooks.php | 5 +-- app/Console/Commands/VerifiesAccessToken.php | 1 - app/Factory/AccountFactory.php | 1 - app/Factory/TransactionJournalFactory.php | 5 ++- app/Handlers/Events/UserEventHandler.php | 3 -- .../Events/VersionCheckEventHandler.php | 10 +++++ app/Helpers/Attachments/AttachmentHelper.php | 8 +++- .../Extensions/AccountCollection.php | 1 - .../Extensions/AttachmentCollection.php | 4 -- app/Helpers/Collector/GroupCollector.php | 5 +++ app/Helpers/Fiscal/FiscalHelper.php | 6 +++ .../Webhook/Sha3SignatureGenerator.php | 2 +- .../Controllers/Account/CreateController.php | 7 ++- .../Controllers/Account/IndexController.php | 18 ++++++-- .../Controllers/Account/ShowController.php | 19 ++++++-- app/Http/Controllers/Admin/HomeController.php | 4 ++ app/Http/Controllers/Admin/UserController.php | 6 +++ app/Http/Controllers/AttachmentController.php | 1 - .../Auth/ForgotPasswordController.php | 8 ++++ app/Http/Controllers/Auth/LoginController.php | 8 +++- .../Controllers/Auth/RegisterController.php | 9 ++++ .../Auth/ResetPasswordController.php | 7 ++- .../Controllers/Auth/TwoFactorController.php | 6 +++ app/Http/Controllers/Bill/IndexController.php | 1 - app/Http/Controllers/Bill/ShowController.php | 7 +++ .../Budget/BudgetLimitController.php | 6 +-- .../Controllers/Budget/IndexController.php | 4 +- .../Controllers/Budget/ShowController.php | 23 ++++++++++ .../Controllers/Category/IndexController.php | 6 +++ .../Category/NoCategoryController.php | 12 +++++ .../Controllers/Category/ShowController.php | 14 ++++++ .../Controllers/Chart/AccountController.php | 9 +++- .../Controllers/Chart/CategoryController.php | 10 ++++- .../Controllers/Chart/PiggyBankController.php | 3 +- .../Controllers/Chart/ReportController.php | 2 +- app/Http/Controllers/DebugController.php | 3 +- app/Http/Controllers/JavascriptController.php | 10 ++++- app/Http/Controllers/Json/BoxController.php | 2 +- app/Http/Controllers/Json/IntroController.php | 2 - app/Http/Controllers/NewUserController.php | 1 - .../ObjectGroup/IndexController.php | 2 +- .../Controllers/PiggyBank/IndexController.php | 1 - .../Controllers/PreferencesController.php | 4 ++ .../Controllers/Profile/MfaController.php | 13 ++++-- app/Http/Controllers/ProfileController.php | 6 ++- .../Controllers/Recurring/IndexController.php | 6 +++ app/Http/Controllers/ReportController.php | 6 +++ app/Http/Controllers/Rule/IndexController.php | 2 +- app/Http/Controllers/TagController.php | 14 ++++++ .../Transaction/CreateController.php | 11 ++++- .../Transaction/EditController.php | 8 ++++ .../Transaction/IndexController.php | 14 ++++++ .../TransactionCurrency/DeleteController.php | 9 ++++ .../TransactionCurrency/EditController.php | 5 +++ .../TransactionCurrency/IndexController.php | 6 +++ app/Http/Middleware/AcceptHeaders.php | 5 +++ app/Http/Requests/ReportFormRequest.php | 5 +++ app/Jobs/DownloadExchangeRates.php | 4 ++ app/Mail/ReportNewJournalsMail.php | 1 + app/Repositories/Account/AccountTasker.php | 2 - .../Budget/AvailableBudgetRepository.php | 1 + app/Repositories/Budget/BudgetRepository.php | 15 ++----- .../Budget/NoBudgetRepository.php | 1 + .../Category/CategoryRepository.php | 15 ++----- .../Currency/CurrencyRepository.php | 7 ++- .../Journal/JournalCLIRepository.php | 5 +-- .../Recurring/RecurringRepository.php | 1 - .../TransactionGroupRepository.php | 5 +-- app/Repositories/User/UserRepository.php | 6 +-- .../UserGroup/UserGroupRepository.php | 3 -- app/Rules/Account/IsValidAccountType.php | 1 - .../Internal/Support/AccountServiceTrait.php | 7 +-- .../Internal/Support/BillServiceTrait.php | 4 +- .../Internal/Support/JournalServiceTrait.php | 7 +++ .../Support/RecurringTransactionTrait.php | 4 +- .../Internal/Update/AccountUpdateService.php | 1 - .../Internal/Update/BillUpdateService.php | 1 - .../Internal/Update/CategoryUpdateService.php | 4 +- app/Support/Binder/CLIToken.php | 1 - .../Category/WholePeriodChartGenerator.php | 4 +- app/Support/Export/ExportDataGenerator.php | 10 +++++ app/Support/Form/CurrencyForm.php | 2 - .../Http/Api/ExchangeRateConverter.php | 2 +- app/Support/Http/Controllers/AugumentData.php | 2 +- .../Http/Controllers/GetConfigurationData.php | 2 +- .../Enrichments/AvailableBudgetEnrichment.php | 4 +- .../JsonApi/Enrichments/BudgetEnrichment.php | 4 +- .../Enrichments/BudgetLimitEnrichment.php | 6 +-- .../Enrichments/CategoryEnrichment.php | 6 +-- .../Enrichments/PiggyBankEnrichment.php | 4 +- .../Enrichments/RecurringEnrichment.php | 4 +- app/Support/Models/BillDateCalculator.php | 2 +- .../RecalculatesAvailableBudgetsTrait.php | 2 +- app/Support/ParseDateString.php | 6 ++- app/Support/Preferences.php | 1 - .../Report/Budget/BudgetReportGenerator.php | 1 - .../Recurring/CalculateXOccurrencesSince.php | 2 +- app/Support/Search/OperatorQuerySearch.php | 1 - .../Search/QueryParser/GdbotsQueryParser.php | 5 +++ app/Support/Steam.php | 6 ++- app/Support/System/OAuthKeys.php | 5 +++ .../PiggyBankEventTransformer.php | 2 +- app/Transformers/PiggyBankTransformer.php | 1 - app/Transformers/RecurrenceTransformer.php | 1 - app/Transformers/UserTransformer.php | 1 - app/User.php | 1 - app/Validation/FireflyValidator.php | 9 +++- 169 files changed, 482 insertions(+), 305 deletions(-) diff --git a/app/Api/V1/Controllers/Data/Export/ExportController.php b/app/Api/V1/Controllers/Data/Export/ExportController.php index f65e23dfc0..f578b7b050 100644 --- a/app/Api/V1/Controllers/Data/Export/ExportController.php +++ b/app/Api/V1/Controllers/Data/Export/ExportController.php @@ -61,8 +61,11 @@ class ExportController extends Controller } /** - * @throws FireflyException + * @param ExportRequest $request * + * @return LaravelResponse + * @throws DatetimeException + * @throws FireflyException * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ public function accounts(ExportRequest $request): LaravelResponse @@ -100,8 +103,11 @@ class ExportController extends Controller } /** - * @throws FireflyException + * @param ExportRequest $request * + * @return LaravelResponse + * @throws DatetimeException + * @throws FireflyException * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ public function bills(ExportRequest $request): LaravelResponse @@ -112,8 +118,11 @@ class ExportController extends Controller } /** - * @throws FireflyException + * @param ExportRequest $request * + * @return LaravelResponse + * @throws DatetimeException + * @throws FireflyException * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ public function budgets(ExportRequest $request): LaravelResponse @@ -124,8 +133,11 @@ class ExportController extends Controller } /** - * @throws FireflyException + * @param ExportRequest $request * + * @return LaravelResponse + * @throws DatetimeException + * @throws FireflyException * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ public function categories(ExportRequest $request): LaravelResponse @@ -136,8 +148,11 @@ class ExportController extends Controller } /** - * @throws FireflyException + * @param ExportRequest $request * + * @return LaravelResponse + * @throws DatetimeException + * @throws FireflyException * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ public function piggyBanks(ExportRequest $request): LaravelResponse @@ -148,8 +163,11 @@ class ExportController extends Controller } /** - * @throws FireflyException + * @param ExportRequest $request * + * @return LaravelResponse + * @throws DatetimeException + * @throws FireflyException * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ public function recurring(ExportRequest $request): LaravelResponse @@ -160,8 +178,11 @@ class ExportController extends Controller } /** - * @throws FireflyException + * @param ExportRequest $request * + * @return LaravelResponse + * @throws DatetimeException + * @throws FireflyException * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ public function rules(ExportRequest $request): LaravelResponse @@ -172,8 +193,11 @@ class ExportController extends Controller } /** - * @throws FireflyException + * @param ExportRequest $request * + * @return LaravelResponse + * @throws DatetimeException + * @throws FireflyException * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ public function tags(ExportRequest $request): LaravelResponse @@ -184,6 +208,10 @@ class ExportController extends Controller } /** + * @param ExportRequest $request + * + * @return LaravelResponse + * @throws DatetimeException * @throws FireflyException */ public function transactions(ExportRequest $request): LaravelResponse diff --git a/app/Api/V1/Controllers/Models/Account/ListController.php b/app/Api/V1/Controllers/Models/Account/ListController.php index eacc1824f1..1d646a0388 100644 --- a/app/Api/V1/Controllers/Models/Account/ListController.php +++ b/app/Api/V1/Controllers/Models/Account/ListController.php @@ -71,7 +71,6 @@ class ListController extends Controller } /** - * @throws FireflyException */ public function attachments(Account $account): JsonResponse { @@ -97,7 +96,6 @@ class ListController extends Controller } /** - * @throws FireflyException */ public function piggyBanks(Account $account): JsonResponse { @@ -136,7 +134,6 @@ class ListController extends Controller /** * Show all transaction groups related to the account. * - * @throws FireflyException */ public function transactions(Request $request, Account $account): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Account/ShowController.php b/app/Api/V1/Controllers/Models/Account/ShowController.php index d2d02ac82a..5ef0363961 100644 --- a/app/Api/V1/Controllers/Models/Account/ShowController.php +++ b/app/Api/V1/Controllers/Models/Account/ShowController.php @@ -69,7 +69,6 @@ class ShowController extends Controller /** * Display a listing of the resource. * - * @throws FireflyException */ public function index(ShowRequest $request): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Attachment/ShowController.php b/app/Api/V1/Controllers/Models/Attachment/ShowController.php index bb8e267a11..3785d30b35 100644 --- a/app/Api/V1/Controllers/Models/Attachment/ShowController.php +++ b/app/Api/V1/Controllers/Models/Attachment/ShowController.php @@ -120,7 +120,6 @@ class ShowController extends Controller * * Display a listing of the resource. * - * @throws FireflyException */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php b/app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php index 1fa1ed692d..c9693643eb 100644 --- a/app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php +++ b/app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php @@ -68,7 +68,6 @@ class ShowController extends Controller * * Display a listing of the resource. * - * @throws FireflyException */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Bill/ListController.php b/app/Api/V1/Controllers/Models/Bill/ListController.php index e0e21e01cc..24f27edb1e 100644 --- a/app/Api/V1/Controllers/Models/Bill/ListController.php +++ b/app/Api/V1/Controllers/Models/Bill/ListController.php @@ -72,7 +72,6 @@ class ListController extends Controller * * Display a listing of the resource. * - * @throws FireflyException */ public function attachments(Bill $bill): JsonResponse { @@ -103,7 +102,6 @@ class ListController extends Controller * * List all of them. * - * @throws FireflyException */ public function rules(Bill $bill): JsonResponse { @@ -136,7 +134,6 @@ class ListController extends Controller * * Show all transactions. * - * @throws FireflyException */ public function transactions(Request $request, Bill $bill): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Bill/ShowController.php b/app/Api/V1/Controllers/Models/Bill/ShowController.php index 6c767f16a6..380d86ef76 100644 --- a/app/Api/V1/Controllers/Models/Bill/ShowController.php +++ b/app/Api/V1/Controllers/Models/Bill/ShowController.php @@ -66,7 +66,6 @@ class ShowController extends Controller * * Display a listing of the resource. * - * @throws FireflyException */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Budget/ListController.php b/app/Api/V1/Controllers/Models/Budget/ListController.php index 036fa6d4ec..72a9cae8fb 100644 --- a/app/Api/V1/Controllers/Models/Budget/ListController.php +++ b/app/Api/V1/Controllers/Models/Budget/ListController.php @@ -74,7 +74,6 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/budgets/listAttachmentByBudget * - * @throws FireflyException */ public function attachments(Budget $budget): JsonResponse { @@ -105,7 +104,6 @@ class ListController extends Controller * * Display a listing of the resource. * - * @throws FireflyException */ public function budgetLimits(Budget $budget): JsonResponse { @@ -141,7 +139,6 @@ class ListController extends Controller * * Show all transactions. * - * @throws FireflyException */ public function transactions(Request $request, Budget $budget): JsonResponse { @@ -203,7 +200,6 @@ class ListController extends Controller * * Show all transactions. * - * @throws FireflyException */ public function withoutBudget(Request $request): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Budget/ShowController.php b/app/Api/V1/Controllers/Models/Budget/ShowController.php index e5062863b1..8d9314b85f 100644 --- a/app/Api/V1/Controllers/Models/Budget/ShowController.php +++ b/app/Api/V1/Controllers/Models/Budget/ShowController.php @@ -70,7 +70,6 @@ class ShowController extends Controller * * Display a listing of the resource. * - * @throws FireflyException */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php b/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php index ee987eae8a..0b66da5487 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php @@ -50,7 +50,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/budgets/listTransactionByBudgetLimit * Show all transactions. * - * @throws FireflyException */ public function transactions(Request $request, Budget $budget, BudgetLimit $budgetLimit): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Category/ListController.php b/app/Api/V1/Controllers/Models/Category/ListController.php index 04af2f2c6c..b89fc38bae 100644 --- a/app/Api/V1/Controllers/Models/Category/ListController.php +++ b/app/Api/V1/Controllers/Models/Category/ListController.php @@ -69,7 +69,6 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/categories/listAttachmentByCategory * - * @throws FireflyException */ public function attachments(Category $category): JsonResponse { @@ -100,7 +99,6 @@ class ListController extends Controller * * Show all transactions. * - * @throws FireflyException */ public function transactions(Request $request, Category $category): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Category/ShowController.php b/app/Api/V1/Controllers/Models/Category/ShowController.php index 9022eed9f9..0d19bb5b77 100644 --- a/app/Api/V1/Controllers/Models/Category/ShowController.php +++ b/app/Api/V1/Controllers/Models/Category/ShowController.php @@ -66,7 +66,6 @@ class ShowController extends Controller * * Display a listing of the resource. * - * @throws FireflyException */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/ObjectGroup/ListController.php b/app/Api/V1/Controllers/Models/ObjectGroup/ListController.php index 78dab17658..81fcc0679a 100644 --- a/app/Api/V1/Controllers/Models/ObjectGroup/ListController.php +++ b/app/Api/V1/Controllers/Models/ObjectGroup/ListController.php @@ -69,7 +69,6 @@ class ListController extends Controller * * List all bills in this object group * - * @throws FireflyException */ public function bills(ObjectGroup $objectGroup): JsonResponse { @@ -110,7 +109,6 @@ class ListController extends Controller * * List all piggies under the object group. * - * @throws FireflyException */ public function piggyBanks(ObjectGroup $objectGroup): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/PiggyBank/ListController.php b/app/Api/V1/Controllers/Models/PiggyBank/ListController.php index 558255ceb4..5d4181a425 100644 --- a/app/Api/V1/Controllers/Models/PiggyBank/ListController.php +++ b/app/Api/V1/Controllers/Models/PiggyBank/ListController.php @@ -68,7 +68,6 @@ class ListController extends Controller * * List single resource. * - * @throws FireflyException */ public function accounts(PiggyBank $piggyBank): JsonResponse { @@ -106,7 +105,6 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/piggy_banks/listAttachmentByPiggyBank * - * @throws FireflyException */ public function attachments(PiggyBank $piggyBank): JsonResponse { @@ -137,7 +135,6 @@ class ListController extends Controller * * List single resource. * - * @throws FireflyException */ public function piggyBankEvents(PiggyBank $piggyBank): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php b/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php index c81d49bea5..570947db73 100644 --- a/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php +++ b/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php @@ -66,7 +66,6 @@ class ShowController extends Controller * * List all of them. * - * @throws FireflyException */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Recurrence/ListController.php b/app/Api/V1/Controllers/Models/Recurrence/ListController.php index 867dc46e33..deb46548a3 100644 --- a/app/Api/V1/Controllers/Models/Recurrence/ListController.php +++ b/app/Api/V1/Controllers/Models/Recurrence/ListController.php @@ -69,7 +69,6 @@ class ListController extends Controller * * Show transactions for this recurrence. * - * @throws FireflyException */ public function transactions(Request $request, Recurrence $recurrence): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Recurrence/ShowController.php b/app/Api/V1/Controllers/Models/Recurrence/ShowController.php index 74f29cface..1be11a85f8 100644 --- a/app/Api/V1/Controllers/Models/Recurrence/ShowController.php +++ b/app/Api/V1/Controllers/Models/Recurrence/ShowController.php @@ -66,7 +66,6 @@ class ShowController extends Controller * * List all of them. * - * @throws FireflyException */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Recurrence/TriggerController.php b/app/Api/V1/Controllers/Models/Recurrence/TriggerController.php index b00aa9767b..e9662efaf1 100644 --- a/app/Api/V1/Controllers/Models/Recurrence/TriggerController.php +++ b/app/Api/V1/Controllers/Models/Recurrence/TriggerController.php @@ -87,9 +87,7 @@ class TriggerController extends Controller // enrich groups and return them: - if (0 === $groups->count()) { - $paginator = new LengthAwarePaginator(new Collection(), 0, 1); - } + $paginator = new LengthAwarePaginator(new Collection(), 0, 1); if ($groups->count() > 0) { /** @var User $admin */ $admin = auth()->user(); diff --git a/app/Api/V1/Controllers/Models/Rule/ShowController.php b/app/Api/V1/Controllers/Models/Rule/ShowController.php index d6ad0db7a5..e659f2e8c4 100644 --- a/app/Api/V1/Controllers/Models/Rule/ShowController.php +++ b/app/Api/V1/Controllers/Models/Rule/ShowController.php @@ -68,7 +68,6 @@ class ShowController extends Controller * * List all of them. * - * @throws FireflyException */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/RuleGroup/ListController.php b/app/Api/V1/Controllers/Models/RuleGroup/ListController.php index af419528a1..6c7b614c03 100644 --- a/app/Api/V1/Controllers/Models/RuleGroup/ListController.php +++ b/app/Api/V1/Controllers/Models/RuleGroup/ListController.php @@ -65,7 +65,6 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/rule_groups/listRuleByGroup * - * @throws FireflyException */ public function rules(RuleGroup $group): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/RuleGroup/ShowController.php b/app/Api/V1/Controllers/Models/RuleGroup/ShowController.php index a906227340..2cf7833853 100644 --- a/app/Api/V1/Controllers/Models/RuleGroup/ShowController.php +++ b/app/Api/V1/Controllers/Models/RuleGroup/ShowController.php @@ -67,7 +67,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/rule_groups/listRuleGroup * List all of them. * - * @throws FireflyException */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Tag/ListController.php b/app/Api/V1/Controllers/Models/Tag/ListController.php index 1fc4629b05..a29b3a016b 100644 --- a/app/Api/V1/Controllers/Models/Tag/ListController.php +++ b/app/Api/V1/Controllers/Models/Tag/ListController.php @@ -72,7 +72,6 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/tags/listAttachmentByTag * - * @throws FireflyException */ public function attachments(Tag $tag): JsonResponse { @@ -103,7 +102,6 @@ class ListController extends Controller * * Show all transactions. * - * @throws FireflyException */ public function transactions(Request $request, Tag $tag): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Tag/ShowController.php b/app/Api/V1/Controllers/Models/Tag/ShowController.php index 5e59c4bf11..b9daf868dc 100644 --- a/app/Api/V1/Controllers/Models/Tag/ShowController.php +++ b/app/Api/V1/Controllers/Models/Tag/ShowController.php @@ -68,7 +68,6 @@ class ShowController extends Controller * * List all of them. * - * @throws FireflyException */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Transaction/ListController.php b/app/Api/V1/Controllers/Models/Transaction/ListController.php index 560324e7ff..a7e0649017 100644 --- a/app/Api/V1/Controllers/Models/Transaction/ListController.php +++ b/app/Api/V1/Controllers/Models/Transaction/ListController.php @@ -70,7 +70,6 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/listAttachmentByTransaction * - * @throws FireflyException */ public function attachments(TransactionGroup $transactionGroup): JsonResponse { @@ -102,7 +101,6 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/listEventByTransaction * - * @throws FireflyException */ public function piggyBankEvents(TransactionGroup $transactionGroup): JsonResponse { @@ -145,7 +143,6 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/listLinksByJournal * - * @throws FireflyException */ public function transactionLinks(TransactionJournal $transactionJournal): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Transaction/ShowController.php b/app/Api/V1/Controllers/Models/Transaction/ShowController.php index b20fab792d..f16b37b81a 100644 --- a/app/Api/V1/Controllers/Models/Transaction/ShowController.php +++ b/app/Api/V1/Controllers/Models/Transaction/ShowController.php @@ -53,7 +53,6 @@ class ShowController extends Controller * * Show all transactions. * - * @throws FireflyException */ public function index(Request $request): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php index 67a6486487..cd8363c8fe 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php @@ -72,7 +72,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/listAccountByCurrency * Display a list of accounts. * - * @throws FireflyException */ public function accounts(Request $request, TransactionCurrency $currency): JsonResponse { @@ -130,7 +129,6 @@ class ListController extends Controller * * Display a listing of the resource. * - * @throws FireflyException */ public function availableBudgets(TransactionCurrency $currency): JsonResponse { @@ -165,7 +163,6 @@ class ListController extends Controller * * List all bills * - * @throws FireflyException */ public function bills(TransactionCurrency $currency): JsonResponse { @@ -212,7 +209,6 @@ class ListController extends Controller * * List all budget limits * - * @throws FireflyException */ public function budgetLimits(TransactionCurrency $currency): JsonResponse { @@ -250,7 +246,6 @@ class ListController extends Controller * * List all recurring transactions. * - * @throws FireflyException */ public function recurrences(TransactionCurrency $currency): JsonResponse { @@ -303,7 +298,6 @@ class ListController extends Controller * * List all of them. * - * @throws FireflyException */ public function rules(TransactionCurrency $currency): JsonResponse { @@ -348,7 +342,6 @@ class ListController extends Controller * * Show all transactions. * - * @throws FireflyException */ public function transactions(Request $request, TransactionCurrency $currency): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php index 481896505c..9fd407ccf4 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php @@ -70,7 +70,6 @@ class ShowController extends Controller * * Display a listing of the resource. * - * @throws FireflyException */ public function index(): JsonResponse { @@ -100,7 +99,6 @@ class ShowController extends Controller * * Show a currency. * - * @throws FireflyException */ public function show(TransactionCurrency $currency): JsonResponse { @@ -124,7 +122,6 @@ class ShowController extends Controller /** * Show a currency. * - * @throws FireflyException */ public function showPrimary(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php index 5e4364043f..01aad04665 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php @@ -67,7 +67,6 @@ class StoreController extends Controller * * Store new currency. * - * @throws FireflyException */ public function store(StoreRequest $request): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php index f9a45ffdee..eca115a7d8 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php @@ -99,7 +99,6 @@ class UpdateController extends Controller } /** - * @throws FireflyException */ public function makePrimary(TransactionCurrency $currency): JsonResponse { @@ -128,7 +127,6 @@ class UpdateController extends Controller * * Enable a currency. * - * @throws FireflyException */ public function enable(TransactionCurrency $currency): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/TransactionLink/ShowController.php b/app/Api/V1/Controllers/Models/TransactionLink/ShowController.php index f5874f90ae..1f812f2b18 100644 --- a/app/Api/V1/Controllers/Models/TransactionLink/ShowController.php +++ b/app/Api/V1/Controllers/Models/TransactionLink/ShowController.php @@ -70,7 +70,6 @@ class ShowController extends Controller * * List all transaction links there are. * - * @throws FireflyException */ public function index(Request $request): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php b/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php index 0cbaae2bea..6ba4e25c4c 100644 --- a/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php +++ b/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php @@ -69,7 +69,6 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/links/listTransactionByLinkType * - * @throws FireflyException */ public function transactions(Request $request, LinkType $linkType): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/TransactionLinkType/ShowController.php b/app/Api/V1/Controllers/Models/TransactionLinkType/ShowController.php index 9fa5e2e497..ded5d53f8f 100644 --- a/app/Api/V1/Controllers/Models/TransactionLinkType/ShowController.php +++ b/app/Api/V1/Controllers/Models/TransactionLinkType/ShowController.php @@ -68,7 +68,6 @@ class ShowController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/links/listLinkType * - * @throws FireflyException */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Search/TransactionController.php b/app/Api/V1/Controllers/Search/TransactionController.php index a68f44d8a6..10d754891a 100644 --- a/app/Api/V1/Controllers/Search/TransactionController.php +++ b/app/Api/V1/Controllers/Search/TransactionController.php @@ -43,7 +43,6 @@ class TransactionController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/search/searchTransactions * - * @throws FireflyException */ public function search(Request $request, SearchInterface $searcher): JsonResponse { diff --git a/app/Api/V1/Controllers/System/UserController.php b/app/Api/V1/Controllers/System/UserController.php index 0928c041a9..edffb84c44 100644 --- a/app/Api/V1/Controllers/System/UserController.php +++ b/app/Api/V1/Controllers/System/UserController.php @@ -91,7 +91,6 @@ class UserController extends Controller * * Display a listing of the resource. * - * @throws FireflyException */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/User/PreferencesController.php b/app/Api/V1/Controllers/User/PreferencesController.php index d2040588e3..552d10c8e0 100644 --- a/app/Api/V1/Controllers/User/PreferencesController.php +++ b/app/Api/V1/Controllers/User/PreferencesController.php @@ -50,7 +50,6 @@ class PreferencesController extends Controller * * List all of them. * - * @throws FireflyException */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Webhook/MessageController.php b/app/Api/V1/Controllers/Webhook/MessageController.php index 6e14814f6f..a5426ca91d 100644 --- a/app/Api/V1/Controllers/Webhook/MessageController.php +++ b/app/Api/V1/Controllers/Webhook/MessageController.php @@ -63,7 +63,6 @@ class MessageController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/webhooks/getWebhookMessages * - * @throws FireflyException */ public function index(Webhook $webhook): JsonResponse { diff --git a/app/Api/V1/Controllers/Webhook/ShowController.php b/app/Api/V1/Controllers/Webhook/ShowController.php index 03249281c3..b4728e2dee 100644 --- a/app/Api/V1/Controllers/Webhook/ShowController.php +++ b/app/Api/V1/Controllers/Webhook/ShowController.php @@ -71,7 +71,6 @@ class ShowController extends Controller * * Display a listing of the webhooks of the user. * - * @throws FireflyException */ public function index(): JsonResponse { diff --git a/app/Console/Commands/Correction/ConvertsDatesToUTC.php b/app/Console/Commands/Correction/ConvertsDatesToUTC.php index 63457c65f3..657d8a0a83 100644 --- a/app/Console/Commands/Correction/ConvertsDatesToUTC.php +++ b/app/Console/Commands/Correction/ConvertsDatesToUTC.php @@ -92,7 +92,6 @@ class ConvertsDatesToUTC extends Command $this->friendlyInfo(sprintf('Converting field "%s" of model "%s" to UTC.', $field, $shortModel)); $items->each( function ($item) use ($field, $timezoneField): void { - /** @var Carbon $date */ $date = Carbon::parse($item->{$field}, $item->{$timezoneField}); // @phpstan-ignore-line $date->setTimezone('UTC'); $item->{$field} = $date->format('Y-m-d H:i:s'); // @phpstan-ignore-line diff --git a/app/Console/Commands/Correction/RemovesOrphanedTransactions.php b/app/Console/Commands/Correction/RemovesOrphanedTransactions.php index f44ee5fb0d..86eadad492 100644 --- a/app/Console/Commands/Correction/RemovesOrphanedTransactions.php +++ b/app/Console/Commands/Correction/RemovesOrphanedTransactions.php @@ -135,9 +135,7 @@ class RemovesOrphanedTransactions extends Command // delete journals /** @var null|TransactionJournal $journal */ $journal = TransactionJournal::find($transaction->transaction_journal_id); - if (null !== $journal) { - $journal->delete(); - } + $journal?->delete(); Transaction::where('transaction_journal_id', $transaction->transaction_journal_id)->delete(); $this->friendlyWarning( sprintf( diff --git a/app/Console/Commands/Export/ExportsData.php b/app/Console/Commands/Export/ExportsData.php index cce69d8e45..18b1df5a34 100644 --- a/app/Console/Commands/Export/ExportsData.php +++ b/app/Console/Commands/Export/ExportsData.php @@ -40,6 +40,7 @@ use Exception; use Illuminate\Support\Facades\Log; use InvalidArgumentException; +use Safe\Exceptions\FilesystemException; use function Safe\file_put_contents; class ExportsData extends Command @@ -275,7 +276,11 @@ class ExportsData extends Command } /** + * @param array $options + * @param array $data + * * @throws FireflyException + * @throws FilesystemException */ private function exportData(array $options, array $data): void { diff --git a/app/Console/Commands/System/CallsLaravelPassportKeys.php b/app/Console/Commands/System/CallsLaravelPassportKeys.php index 22e1ebddd3..2d48051fe2 100644 --- a/app/Console/Commands/System/CallsLaravelPassportKeys.php +++ b/app/Console/Commands/System/CallsLaravelPassportKeys.php @@ -42,7 +42,7 @@ class CallsLaravelPassportKeys extends Command */ public function handle(): int { - Artisan::call('passport:keys --no-interaction', []); + Artisan::call('passport:keys --no-interaction'); $result = Artisan::output(); if (str_contains($result, 'Encryption keys already exist')) { $this->friendlyInfo('Encryption keys exist already.'); diff --git a/app/Console/Commands/System/ForcesDecimalSize.php b/app/Console/Commands/System/ForcesDecimalSize.php index b96ace66da..4f1ec2164e 100644 --- a/app/Console/Commands/System/ForcesDecimalSize.php +++ b/app/Console/Commands/System/ForcesDecimalSize.php @@ -156,7 +156,6 @@ class ForcesDecimalSize extends Command */ private function correctAmountsByCurrency(): void { - /** @var Collection $enabled */ $enabled = TransactionCurrency::whereEnabled(1)->get(); /** @var TransactionCurrency $currency */ @@ -301,7 +300,7 @@ class ForcesDecimalSize extends Command } ); - $result = $query->get(['*']); + $result = $query->get(); if (0 === $result->count()) { $this->friendlyPositive(sprintf('All %s in %s are OK', $table, $currency->code)); @@ -533,7 +532,7 @@ class ForcesDecimalSize extends Command DB::raw(sprintf($this->regularExpression, $currency->decimal_places)) ); - $result = $query->get(['*']); + $result = $query->get(); if (0 === $result->count()) { $this->friendlyPositive(sprintf('All transactions in foreign currency %s are OK', $currency->code)); diff --git a/app/Console/Commands/System/VerifySecurityAlerts.php b/app/Console/Commands/System/VerifySecurityAlerts.php index 75b7f85b10..ee739ef9a3 100644 --- a/app/Console/Commands/System/VerifySecurityAlerts.php +++ b/app/Console/Commands/System/VerifySecurityAlerts.php @@ -31,6 +31,7 @@ use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; use League\Flysystem\FilesystemException; +use Safe\Exceptions\JsonException; use function Safe\json_decode; class VerifySecurityAlerts extends Command @@ -44,7 +45,9 @@ class VerifySecurityAlerts extends Command /** * Execute the console command. * + * @return int * @throws FilesystemException + * @throws JsonException */ public function handle(): int { diff --git a/app/Console/Commands/Upgrade/AddsTransactionIdentifiers.php b/app/Console/Commands/Upgrade/AddsTransactionIdentifiers.php index ebcc347d0a..4ffa9a2981 100644 --- a/app/Console/Commands/Upgrade/AddsTransactionIdentifiers.php +++ b/app/Console/Commands/Upgrade/AddsTransactionIdentifiers.php @@ -54,7 +54,6 @@ class AddsTransactionIdentifiers extends Command * When either of these are the same amount, FF3 can't keep them apart: +3/-3, +3/-3, +3/-3. This happens more * often than you would think. So each set gets a number (1,2,3) to keep them apart. * - * @throws FireflyException */ public function handle(): int { @@ -101,11 +100,8 @@ class AddsTransactionIdentifiers extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } + return (bool)$configVar?->data; - return false; } /** diff --git a/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php b/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php index 57463c5d8d..22b335f8d2 100644 --- a/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php +++ b/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php @@ -99,11 +99,8 @@ class RemovesDatabaseDecryption extends Command } catch (FireflyException $e) { Log::error($e->getMessage()); } - if (null !== $configVar) { - return (bool) $configVar->data; - } + return (bool)$configVar?->data; - return false; } private function decryptField(string $table, string $field): void diff --git a/app/Console/Commands/Upgrade/UpgradesAccountCurrencies.php b/app/Console/Commands/Upgrade/UpgradesAccountCurrencies.php index eef26f8608..c67aae9280 100644 --- a/app/Console/Commands/Upgrade/UpgradesAccountCurrencies.php +++ b/app/Console/Commands/Upgrade/UpgradesAccountCurrencies.php @@ -100,7 +100,6 @@ class UpgradesAccountCurrencies extends Command } /** - * @throws FireflyException */ private function updateCurrenciesForUser(User $user): void { diff --git a/app/Console/Commands/Upgrade/UpgradesAccountMetaData.php b/app/Console/Commands/Upgrade/UpgradesAccountMetaData.php index d181e41e22..7e8f6168cc 100644 --- a/app/Console/Commands/Upgrade/UpgradesAccountMetaData.php +++ b/app/Console/Commands/Upgrade/UpgradesAccountMetaData.php @@ -83,11 +83,8 @@ class UpgradesAccountMetaData extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } + return (bool)$configVar?->data; - return false; } private function markAsExecuted(): void diff --git a/app/Console/Commands/Upgrade/UpgradesAttachments.php b/app/Console/Commands/Upgrade/UpgradesAttachments.php index 06f8175e08..b9cfea1ed6 100644 --- a/app/Console/Commands/Upgrade/UpgradesAttachments.php +++ b/app/Console/Commands/Upgrade/UpgradesAttachments.php @@ -93,11 +93,8 @@ class UpgradesAttachments extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } + return (bool)$configVar?->data; - return false; } private function markAsExecuted(): void diff --git a/app/Console/Commands/Upgrade/UpgradesBillsToRules.php b/app/Console/Commands/Upgrade/UpgradesBillsToRules.php index b146f11e8b..62d43c1889 100644 --- a/app/Console/Commands/Upgrade/UpgradesBillsToRules.php +++ b/app/Console/Commands/Upgrade/UpgradesBillsToRules.php @@ -99,17 +99,13 @@ class UpgradesBillsToRules extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } + return (bool)$configVar?->data; - return false; } /** * Migrate bills to new rule structure for a specific user. * - * @throws FireflyException */ private function migrateUser(User $user): void { diff --git a/app/Console/Commands/Upgrade/UpgradesBudgetLimits.php b/app/Console/Commands/Upgrade/UpgradesBudgetLimits.php index 473247de6f..6430407ea4 100644 --- a/app/Console/Commands/Upgrade/UpgradesBudgetLimits.php +++ b/app/Console/Commands/Upgrade/UpgradesBudgetLimits.php @@ -85,11 +85,8 @@ class UpgradesBudgetLimits extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } + return (bool)$configVar?->data; - return false; } private function markAsExecuted(): void diff --git a/app/Console/Commands/Upgrade/UpgradesCurrencyPreferences.php b/app/Console/Commands/Upgrade/UpgradesCurrencyPreferences.php index 6e2527c8f4..ddecd04fef 100644 --- a/app/Console/Commands/Upgrade/UpgradesCurrencyPreferences.php +++ b/app/Console/Commands/Upgrade/UpgradesCurrencyPreferences.php @@ -66,11 +66,8 @@ class UpgradesCurrencyPreferences extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool)$configVar->data; - } + return (bool)$configVar?->data; - return false; } private function runUpgrade(): void diff --git a/app/Console/Commands/Upgrade/UpgradesJournalNotes.php b/app/Console/Commands/Upgrade/UpgradesJournalNotes.php index 5a3390e3d6..6ae07abb09 100644 --- a/app/Console/Commands/Upgrade/UpgradesJournalNotes.php +++ b/app/Console/Commands/Upgrade/UpgradesJournalNotes.php @@ -87,11 +87,8 @@ class UpgradesJournalNotes extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } + return (bool)$configVar?->data; - return false; } private function markAsExecuted(): void diff --git a/app/Console/Commands/Upgrade/UpgradesLiabilities.php b/app/Console/Commands/Upgrade/UpgradesLiabilities.php index 4a4074b1fb..aaf5f7b221 100644 --- a/app/Console/Commands/Upgrade/UpgradesLiabilities.php +++ b/app/Console/Commands/Upgrade/UpgradesLiabilities.php @@ -62,11 +62,8 @@ class UpgradesLiabilities extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } + return (bool)$configVar?->data; - return false; } private function upgradeLiabilities(): void diff --git a/app/Console/Commands/Upgrade/UpgradesLiabilitiesEight.php b/app/Console/Commands/Upgrade/UpgradesLiabilitiesEight.php index 072c48ca08..571fb94ec8 100644 --- a/app/Console/Commands/Upgrade/UpgradesLiabilitiesEight.php +++ b/app/Console/Commands/Upgrade/UpgradesLiabilitiesEight.php @@ -64,11 +64,8 @@ class UpgradesLiabilitiesEight extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } + return (bool)$configVar?->data; - return false; } private function upgradeLiabilities(): void diff --git a/app/Console/Commands/Upgrade/UpgradesMultiPiggyBanks.php b/app/Console/Commands/Upgrade/UpgradesMultiPiggyBanks.php index d155510181..ecceb1a085 100644 --- a/app/Console/Commands/Upgrade/UpgradesMultiPiggyBanks.php +++ b/app/Console/Commands/Upgrade/UpgradesMultiPiggyBanks.php @@ -65,11 +65,8 @@ class UpgradesMultiPiggyBanks extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } + return (bool)$configVar?->data; - return false; } private function upgradePiggyBanks(): void diff --git a/app/Console/Commands/Upgrade/UpgradesPrimaryCurrencyAmounts.php b/app/Console/Commands/Upgrade/UpgradesPrimaryCurrencyAmounts.php index 9cb26669ee..c233272cba 100644 --- a/app/Console/Commands/Upgrade/UpgradesPrimaryCurrencyAmounts.php +++ b/app/Console/Commands/Upgrade/UpgradesPrimaryCurrencyAmounts.php @@ -61,11 +61,8 @@ class UpgradesPrimaryCurrencyAmounts extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } + return (bool)$configVar?->data; - return false; } private function markAsExecuted(): void diff --git a/app/Console/Commands/Upgrade/UpgradesRecurrenceMetaData.php b/app/Console/Commands/Upgrade/UpgradesRecurrenceMetaData.php index d40f566a4c..82dcb3d160 100644 --- a/app/Console/Commands/Upgrade/UpgradesRecurrenceMetaData.php +++ b/app/Console/Commands/Upgrade/UpgradesRecurrenceMetaData.php @@ -66,11 +66,8 @@ class UpgradesRecurrenceMetaData extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } + return (bool)$configVar?->data; - return false; } private function migrateMetaData(): int diff --git a/app/Console/Commands/Upgrade/UpgradesRuleActions.php b/app/Console/Commands/Upgrade/UpgradesRuleActions.php index c3619be8f9..7baeb2cbe2 100644 --- a/app/Console/Commands/Upgrade/UpgradesRuleActions.php +++ b/app/Console/Commands/Upgrade/UpgradesRuleActions.php @@ -64,11 +64,8 @@ class UpgradesRuleActions extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } + return (bool)$configVar?->data; - return false; } private function replaceEqualSign(): void diff --git a/app/Console/Commands/Upgrade/UpgradesTagLocations.php b/app/Console/Commands/Upgrade/UpgradesTagLocations.php index a6fe336a49..cd60c5b825 100644 --- a/app/Console/Commands/Upgrade/UpgradesTagLocations.php +++ b/app/Console/Commands/Upgrade/UpgradesTagLocations.php @@ -58,11 +58,8 @@ class UpgradesTagLocations extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } + return (bool)$configVar?->data; - return false; } private function migrateTagLocations(): void diff --git a/app/Console/Commands/Upgrade/UpgradesToGroups.php b/app/Console/Commands/Upgrade/UpgradesToGroups.php index 3ceee64711..875df04cc4 100644 --- a/app/Console/Commands/Upgrade/UpgradesToGroups.php +++ b/app/Console/Commands/Upgrade/UpgradesToGroups.php @@ -98,11 +98,8 @@ class UpgradesToGroups extends Command private function isMigrated(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } + return (bool)$configVar?->data; - return false; } /** diff --git a/app/Console/Commands/Upgrade/UpgradesTransferCurrencies.php b/app/Console/Commands/Upgrade/UpgradesTransferCurrencies.php index fee9fde03d..1aced94507 100644 --- a/app/Console/Commands/Upgrade/UpgradesTransferCurrencies.php +++ b/app/Console/Commands/Upgrade/UpgradesTransferCurrencies.php @@ -106,11 +106,8 @@ class UpgradesTransferCurrencies extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } + return (bool)$configVar?->data; - return false; } /** diff --git a/app/Console/Commands/Upgrade/UpgradesVariousCurrencyInformation.php b/app/Console/Commands/Upgrade/UpgradesVariousCurrencyInformation.php index 648b34ed5e..49c4e0cf28 100644 --- a/app/Console/Commands/Upgrade/UpgradesVariousCurrencyInformation.php +++ b/app/Console/Commands/Upgrade/UpgradesVariousCurrencyInformation.php @@ -87,11 +87,8 @@ class UpgradesVariousCurrencyInformation extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } + return (bool)$configVar?->data; - return false; } /** diff --git a/app/Console/Commands/Upgrade/UpgradesWebhooks.php b/app/Console/Commands/Upgrade/UpgradesWebhooks.php index 46e39e11e7..6dd824473a 100644 --- a/app/Console/Commands/Upgrade/UpgradesWebhooks.php +++ b/app/Console/Commands/Upgrade/UpgradesWebhooks.php @@ -64,11 +64,8 @@ class UpgradesWebhooks extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool)$configVar->data; - } + return (bool)$configVar?->data; - return false; } private function upgradeWebhooks(): void diff --git a/app/Console/Commands/VerifiesAccessToken.php b/app/Console/Commands/VerifiesAccessToken.php index b591978e4a..2dcb004d35 100644 --- a/app/Console/Commands/VerifiesAccessToken.php +++ b/app/Console/Commands/VerifiesAccessToken.php @@ -65,7 +65,6 @@ trait VerifiesAccessToken /** * Returns false when given token does not match given user token. * - * @throws FireflyException */ protected function verifyAccessToken(): bool { diff --git a/app/Factory/AccountFactory.php b/app/Factory/AccountFactory.php index 3ea9d77a49..ce47fd2e35 100644 --- a/app/Factory/AccountFactory.php +++ b/app/Factory/AccountFactory.php @@ -83,7 +83,6 @@ class AccountFactory if (null === $return) { Log::debug('Found nothing. Will create a new one.'); - /** @var Account $return */ $return = $this->create( [ 'user_id' => $this->user->id, diff --git a/app/Factory/TransactionJournalFactory.php b/app/Factory/TransactionJournalFactory.php index dd2d9e6830..1c7f354c34 100644 --- a/app/Factory/TransactionJournalFactory.php +++ b/app/Factory/TransactionJournalFactory.php @@ -343,7 +343,11 @@ class TransactionJournalFactory /** * If this transaction already exists, throw an error. * + * @param string $hash + * * @throws DuplicateTransactionException + * @throws JsonException + * @throws \Safe\Exceptions\JsonException */ private function errorIfDuplicate(string $hash): void { @@ -478,7 +482,6 @@ class TransactionJournalFactory } /** - * @throws FireflyException */ private function getCurrency(?TransactionCurrency $currency, Account $account): TransactionCurrency { diff --git a/app/Handlers/Events/UserEventHandler.php b/app/Handlers/Events/UserEventHandler.php index 991f7751d9..775cdc878e 100644 --- a/app/Handlers/Events/UserEventHandler.php +++ b/app/Handlers/Events/UserEventHandler.php @@ -165,7 +165,6 @@ class UserEventHandler /** * Set the demo user back to English. * - * @throws FireflyException */ public function demoUserBackToEnglish(Login $event): void { @@ -183,7 +182,6 @@ class UserEventHandler } /** - * @throws FireflyException */ public function notifyNewIPAddress(DetectedNewIPAddress $event): void { @@ -448,7 +446,6 @@ class UserEventHandler } /** - * @throws FireflyException */ public function storeUserIPAddress(ActuallyLoggedIn $event): void { diff --git a/app/Handlers/Events/VersionCheckEventHandler.php b/app/Handlers/Events/VersionCheckEventHandler.php index a7ac2c62c3..1d663855b0 100644 --- a/app/Handlers/Events/VersionCheckEventHandler.php +++ b/app/Handlers/Events/VersionCheckEventHandler.php @@ -31,6 +31,8 @@ use FireflyIII\Helpers\Update\UpdateTrait; use FireflyIII\Models\Configuration; use FireflyIII\Repositories\User\UserRepositoryInterface; use Illuminate\Support\Facades\Log; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; /** * Class VersionCheckEventHandler @@ -42,7 +44,11 @@ class VersionCheckEventHandler /** * Checks with GitHub to see if there is a new version. * + * @param RequestedVersionCheckStatus $event + * + * @throws ContainerExceptionInterface * @throws FireflyException + * @throws NotFoundExceptionInterface */ #[Deprecated(message: '?')] public function checkForUpdates(RequestedVersionCheckStatus $event): void @@ -87,7 +93,11 @@ class VersionCheckEventHandler } /** + * @param RequestedVersionCheckStatus $event + * * @throws FireflyException + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ protected function warnToCheckForUpdates(RequestedVersionCheckStatus $event): void { diff --git a/app/Helpers/Attachments/AttachmentHelper.php b/app/Helpers/Attachments/AttachmentHelper.php index 145d39063a..846be0c312 100644 --- a/app/Helpers/Attachments/AttachmentHelper.php +++ b/app/Helpers/Attachments/AttachmentHelper.php @@ -37,6 +37,7 @@ use Illuminate\Support\Facades\Storage; use Illuminate\Support\MessageBag; use Safe\Exceptions\FileinfoException; use Safe\Exceptions\FilesystemException; +use Safe\Exceptions\StringsException; use Symfony\Component\HttpFoundation\File\UploadedFile; use function Safe\tmpfile; @@ -222,8 +223,11 @@ class AttachmentHelper implements AttachmentHelperInterface /** * Process the upload of a file. * - * @throws FireflyException - * @throws EncryptException + * @param UploadedFile $file + * @param Model $model + * + * @return Attachment|null + * @throws StringsException */ protected function processFile(UploadedFile $file, Model $model): ?Attachment { diff --git a/app/Helpers/Collector/Extensions/AccountCollection.php b/app/Helpers/Collector/Extensions/AccountCollection.php index 2328b5dabc..d56ee1de4b 100644 --- a/app/Helpers/Collector/Extensions/AccountCollection.php +++ b/app/Helpers/Collector/Extensions/AccountCollection.php @@ -43,7 +43,6 @@ trait AccountCollection Log::warning(sprintf('GroupCollector will be SLOW: accountBalanceIs: "%s" "%s" "%s"', $direction, $operator, $value)); /** - * @param int $index * @param array $object * * @return bool diff --git a/app/Helpers/Collector/Extensions/AttachmentCollection.php b/app/Helpers/Collector/Extensions/AttachmentCollection.php index f4993d2d18..b2b3ed0103 100644 --- a/app/Helpers/Collector/Extensions/AttachmentCollection.php +++ b/app/Helpers/Collector/Extensions/AttachmentCollection.php @@ -41,7 +41,6 @@ trait AttachmentCollection $this->withAttachmentInformation(); /** - * @param int $index * @param array $object * * @return bool @@ -120,7 +119,6 @@ trait AttachmentCollection $this->withAttachmentInformation(); /** - * @param int $index * @param array $object * * @return bool @@ -155,7 +153,6 @@ trait AttachmentCollection $this->withAttachmentInformation(); /** - * @param int $index * @param array $object * * @return bool @@ -190,7 +187,6 @@ trait AttachmentCollection $this->withAttachmentInformation(); /** - * @param int $index * @param array $object * * @return bool diff --git a/app/Helpers/Collector/GroupCollector.php b/app/Helpers/Collector/GroupCollector.php index 0d12819740..0dd40ee445 100644 --- a/app/Helpers/Collector/GroupCollector.php +++ b/app/Helpers/Collector/GroupCollector.php @@ -48,6 +48,7 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use Override; +use Safe\Exceptions\JsonException; use function Safe\json_decode; /** @@ -564,7 +565,11 @@ class GroupCollector implements GroupCollectorInterface } /** + * @param TransactionJournal $augumentedJournal + * + * @return array * @throws FireflyException + * @throws JsonException */ private function parseAugmentedJournal(TransactionJournal $augumentedJournal): array { diff --git a/app/Helpers/Fiscal/FiscalHelper.php b/app/Helpers/Fiscal/FiscalHelper.php index 13c9d14bdc..ae7ebc752b 100644 --- a/app/Helpers/Fiscal/FiscalHelper.php +++ b/app/Helpers/Fiscal/FiscalHelper.php @@ -24,6 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Helpers\Fiscal; use Carbon\Carbon; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; /** * Class FiscalHelper. @@ -62,7 +64,11 @@ class FiscalHelper implements FiscalHelperInterface } /** + * @param Carbon $date + * * @return Carbon date object + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function startOfFiscalYear(Carbon $date): Carbon { diff --git a/app/Helpers/Webhook/Sha3SignatureGenerator.php b/app/Helpers/Webhook/Sha3SignatureGenerator.php index 6d42a4ac56..06967cff04 100644 --- a/app/Helpers/Webhook/Sha3SignatureGenerator.php +++ b/app/Helpers/Webhook/Sha3SignatureGenerator.php @@ -68,7 +68,7 @@ class Sha3SignatureGenerator implements SignatureGeneratorInterface // The actual JSON payload (i.e., the request body) $timestamp = Carbon::now()->getTimestamp(); $payload = sprintf('%s.%s', $timestamp, $json); - $signature = hash_hmac('sha3-256', $payload, (string) $message->webhook->secret, false); + $signature = hash_hmac('sha3-256', $payload, (string) $message->webhook->secret); // signature string: // header included in each signed event contains a timestamp and one or more signatures. diff --git a/app/Http/Controllers/Account/CreateController.php b/app/Http/Controllers/Account/CreateController.php index 743c2fdc7a..e0cb55afc7 100644 --- a/app/Http/Controllers/Account/CreateController.php +++ b/app/Http/Controllers/Account/CreateController.php @@ -37,6 +37,8 @@ use Illuminate\Http\Request; use Illuminate\Routing\Redirector; use Illuminate\Support\Facades\Log; use Illuminate\View\View; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; /** * Class CreateController @@ -131,9 +133,12 @@ class CreateController extends Controller /** * Store the new account. * + * @param AccountFormRequest $request + * * @return Redirector|RedirectResponse * - * @throws FireflyException + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function store(AccountFormRequest $request) { diff --git a/app/Http/Controllers/Account/IndexController.php b/app/Http/Controllers/Account/IndexController.php index 1ee5f5cccf..66844d8ad2 100644 --- a/app/Http/Controllers/Account/IndexController.php +++ b/app/Http/Controllers/Account/IndexController.php @@ -35,6 +35,8 @@ use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Facades\Log; use Illuminate\View\View; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; /** * Class IndexController @@ -66,10 +68,14 @@ class IndexController extends Controller } /** + * @param Request $request + * @param string $objectType + * * @return Factory|View * - * @throws FireflyException - * */ + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ public function inactive(Request $request, string $objectType) { $inactivePage = true; @@ -136,10 +142,14 @@ class IndexController extends Controller /** * Show list of accounts. * + * @param Request $request + * @param string $objectType + * * @return Factory|View * - * @throws FireflyException - * */ + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ public function index(Request $request, string $objectType) { app('log')->debug(sprintf('Now at %s', __METHOD__)); diff --git a/app/Http/Controllers/Account/ShowController.php b/app/Http/Controllers/Account/ShowController.php index 8c9ac76727..8181e0ff80 100644 --- a/app/Http/Controllers/Account/ShowController.php +++ b/app/Http/Controllers/Account/ShowController.php @@ -40,6 +40,8 @@ use Illuminate\Routing\Redirector; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use Illuminate\View\View; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -76,10 +78,17 @@ class ShowController extends Controller /** * Show an account. * + * @param Request $request + * @param Account $account + * @param Carbon|null $start + * @param Carbon|null $end + * * @return Factory|Redirector|RedirectResponse|View * + * @throws ContainerExceptionInterface * @throws FireflyException - * */ + * @throws NotFoundExceptionInterface + */ public function show(Request $request, Account $account, ?Carbon $start = null, ?Carbon $end = null) { if (0 === $account->id) { @@ -193,10 +202,14 @@ class ShowController extends Controller /** * Show an account. * + * @param Request $request + * @param Account $account + * * @return Factory|Redirector|RedirectResponse|View * - * @throws FireflyException - * */ + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ public function showAll(Request $request, Account $account) { if (!$this->isEditableAccount($account)) { diff --git a/app/Http/Controllers/Admin/HomeController.php b/app/Http/Controllers/Admin/HomeController.php index 6f4fc059f2..264d79158d 100644 --- a/app/Http/Controllers/Admin/HomeController.php +++ b/app/Http/Controllers/Admin/HomeController.php @@ -28,6 +28,8 @@ use FireflyIII\Http\Middleware\IsDemoUser; use Illuminate\Contracts\View\Factory; use Illuminate\Support\Facades\Log; use Illuminate\View\View; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; /** * Class HomeController. @@ -47,6 +49,8 @@ class HomeController extends Controller * Index of the admin. * * @return Factory|View + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function index() { diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 07abf76b4d..54ad535864 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Admin; use FireflyIII\Events\Admin\InvitationCreated; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Middleware\IsDemoUser; use FireflyIII\Http\Requests\InviteUserFormRequest; @@ -37,6 +38,8 @@ use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; use Illuminate\Routing\Redirector; use Illuminate\View\View; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; /** * Class UserController. @@ -151,6 +154,9 @@ class UserController extends Controller * Show index of user manager. * * @return Factory|View + * @throws ContainerExceptionInterface + * @throws FireflyException + * @throws NotFoundExceptionInterface */ public function index() { diff --git a/app/Http/Controllers/AttachmentController.php b/app/Http/Controllers/AttachmentController.php index c45b947126..b06fc9a007 100644 --- a/app/Http/Controllers/AttachmentController.php +++ b/app/Http/Controllers/AttachmentController.php @@ -194,7 +194,6 @@ class AttachmentController extends Controller * View attachment in browser. * * @throws FireflyException - * @throws BindingResolutionException */ public function view(Attachment $attachment): LaravelResponse { diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php index 78fcc649db..1b3607259c 100644 --- a/app/Http/Controllers/Auth/ForgotPasswordController.php +++ b/app/Http/Controllers/Auth/ForgotPasswordController.php @@ -33,6 +33,8 @@ use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; use Illuminate\View\View; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; use Safe\Exceptions\UrlException; use function Safe\parse_url; @@ -60,7 +62,11 @@ class ForgotPasswordController extends Controller /** * Send a reset link to the given user. * + * @param Request $request + * @param UserRepositoryInterface $repository + * * @return Factory|RedirectResponse|View + * @throws FireflyException */ public function sendResetLinkEmail(Request $request, UserRepositoryInterface $repository) { @@ -126,6 +132,8 @@ class ForgotPasswordController extends Controller * @return Factory|View * * @throws FireflyException + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function showLinkRequestForm() { diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index c1566b5ed1..01b25bbd19 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -46,6 +46,8 @@ use Illuminate\Routing\Redirector; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; use Illuminate\Validation\ValidationException; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; use Symfony\Component\HttpFoundation\Response as ResponseAlias; /** @@ -151,7 +153,7 @@ class LoginController extends Controller $this->sendFailedLoginResponse($request); // @noinspection PhpUnreachableStatementInspection - return response()->json([]); + return response()->json(); } /** @@ -219,9 +221,13 @@ class LoginController extends Controller /** * Show the application's login form. * + * @param Request $request + * * @return Application|Factory|Redirector|RedirectResponse|View * * @throws FireflyException + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function showLoginForm(Request $request) { diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php index 79701fe3f7..4122f3fa5a 100644 --- a/app/Http/Controllers/Auth/RegisterController.php +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -140,9 +140,14 @@ class RegisterController extends Controller /** * Show the application registration form if the invitation code is valid. * + * @param Request $request + * @param string $code + * * @return Factory|View * + * @throws ContainerExceptionInterface * @throws FireflyException + * @throws NotFoundExceptionInterface */ public function showInviteForm(Request $request, string $code) { @@ -172,9 +177,13 @@ class RegisterController extends Controller /** * Show the application registration form. * + * @param Request|null $request + * * @return Factory|View * + * @throws ContainerExceptionInterface * @throws FireflyException + * @throws NotFoundExceptionInterface */ public function showRegistrationForm(?Request $request = null) { diff --git a/app/Http/Controllers/Auth/ResetPasswordController.php b/app/Http/Controllers/Auth/ResetPasswordController.php index 442d4956b4..7ff28e0264 100644 --- a/app/Http/Controllers/Auth/ResetPasswordController.php +++ b/app/Http/Controllers/Auth/ResetPasswordController.php @@ -34,6 +34,8 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Password; use Illuminate\Validation\ValidationException; use Illuminate\View\View; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; /** * Class ResetPasswordController @@ -112,11 +114,14 @@ class ResetPasswordController extends Controller * * If no token is present, display the link request form. * - * @param null $token + * @param Request $request + * @param null $token * * @return Factory|View * * @throws FireflyException + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function showResetForm(Request $request, $token = null) { diff --git a/app/Http/Controllers/Auth/TwoFactorController.php b/app/Http/Controllers/Auth/TwoFactorController.php index 1ebce31552..d9cd52c035 100644 --- a/app/Http/Controllers/Auth/TwoFactorController.php +++ b/app/Http/Controllers/Auth/TwoFactorController.php @@ -37,6 +37,8 @@ use Illuminate\Http\Request; use Illuminate\Routing\Redirector; use Illuminate\Support\Facades\Log; use PragmaRX\Google2FALaravel\Support\Authenticator; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; /** * Class TwoFactorController. @@ -59,7 +61,11 @@ class TwoFactorController extends Controller } /** + * @param Request $request + * * @return Redirector|RedirectResponse + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function submitMFA(Request $request) { diff --git a/app/Http/Controllers/Bill/IndexController.php b/app/Http/Controllers/Bill/IndexController.php index 46f8514f14..1725bad953 100644 --- a/app/Http/Controllers/Bill/IndexController.php +++ b/app/Http/Controllers/Bill/IndexController.php @@ -149,7 +149,6 @@ class IndexController extends Controller } /** - * @throws FireflyException */ private function getSums(array $bills): array { diff --git a/app/Http/Controllers/Bill/ShowController.php b/app/Http/Controllers/Bill/ShowController.php index acfb3ab3aa..ef004709f5 100644 --- a/app/Http/Controllers/Bill/ShowController.php +++ b/app/Http/Controllers/Bill/ShowController.php @@ -44,6 +44,8 @@ use Illuminate\View\View; use League\Fractal\Manager; use League\Fractal\Resource\Item; use League\Fractal\Serializer\DataArraySerializer; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; use Symfony\Component\HttpFoundation\ParameterBag; /** @@ -113,7 +115,12 @@ class ShowController extends Controller /** * Show a bill. * + * @param Request $request + * @param Bill $bill + * * @return Factory|View + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function show(Request $request, Bill $bill) { diff --git a/app/Http/Controllers/Budget/BudgetLimitController.php b/app/Http/Controllers/Budget/BudgetLimitController.php index 63e0c93e7e..81087328c4 100644 --- a/app/Http/Controllers/Budget/BudgetLimitController.php +++ b/app/Http/Controllers/Budget/BudgetLimitController.php @@ -150,7 +150,7 @@ class BudgetLimitController extends Controller $end = Carbon::createFromFormat('Y-m-d', $request->get('end')); if (!$start instanceof Carbon || !$end instanceof Carbon) { - return response()->json([]); + return response()->json(); } $amount = (string) $request->get('amount'); @@ -158,7 +158,7 @@ class BudgetLimitController extends Controller $end->startOfDay(); if ('' === $amount) { - return response()->json([]); + return response()->json(); } app('log')->debug(sprintf('Start: %s, end: %s', $start->format('Y-m-d'), $end->format('Y-m-d'))); @@ -172,7 +172,7 @@ class BudgetLimitController extends Controller } // return empty=ish array: - return response()->json([]); + return response()->json(); } if ((int) $amount > 268435456) { // intentional cast to integer $amount = '268435456'; diff --git a/app/Http/Controllers/Budget/IndexController.php b/app/Http/Controllers/Budget/IndexController.php index 2ba33347a1..08631b6bd3 100644 --- a/app/Http/Controllers/Budget/IndexController.php +++ b/app/Http/Controllers/Budget/IndexController.php @@ -174,7 +174,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, false); + $spentArr = $this->opsRepository->sumExpenses($entry->start_date, $entry->end_date, null, null, $entry->transactionCurrency); $array['spent'] = $spentArr[$entry->transaction_currency_id]['sum'] ?? '0'; $array['pc_spent'] = $this->convertToPrimary && $entry->transaction_currency_id !== $this->primaryCurrency->id ? $converter->convert($entry->transactionCurrency, $this->primaryCurrency, $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()->push($current), $currency, false); + $spentArr = $this->opsRepository->sumExpenses($start, $end, null, new Collection()->push($current), $currency); 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; diff --git a/app/Http/Controllers/Budget/ShowController.php b/app/Http/Controllers/Budget/ShowController.php index ce344f13e8..c28e7455e8 100644 --- a/app/Http/Controllers/Budget/ShowController.php +++ b/app/Http/Controllers/Budget/ShowController.php @@ -39,6 +39,8 @@ use FireflyIII\Support\Http\Controllers\PeriodOverview; use Illuminate\Contracts\View\Factory; use Illuminate\Http\Request; use Illuminate\View\View; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; /** * Class ShowController @@ -73,9 +75,15 @@ class ShowController extends Controller /** * Show transactions without a budget. * + * @param Request $request + * @param Carbon|null $start + * @param Carbon|null $end + * * @return Factory|View * + * @throws ContainerExceptionInterface * @throws FireflyException + * @throws NotFoundExceptionInterface */ public function noBudget(Request $request, ?Carbon $start = null, ?Carbon $end = null) { @@ -110,7 +118,11 @@ class ShowController extends Controller /** * Shows ALL transactions without a budget. * + * @param Request $request + * * @return Factory|View + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function noBudgetAll(Request $request) { @@ -135,7 +147,12 @@ class ShowController extends Controller /** * Show a single budget. * + * @param Request $request + * @param Budget $budget + * * @return Factory|View + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function show(Request $request, Budget $budget) { @@ -166,9 +183,15 @@ class ShowController extends Controller /** * Show a single budget by a budget limit. * + * @param Request $request + * @param Budget $budget + * @param BudgetLimit $budgetLimit + * * @return Factory|View * * @throws FireflyException + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function showByBudgetLimit(Request $request, Budget $budget, BudgetLimit $budgetLimit) { diff --git a/app/Http/Controllers/Category/IndexController.php b/app/Http/Controllers/Category/IndexController.php index 41b85c87a7..ecd406df91 100644 --- a/app/Http/Controllers/Category/IndexController.php +++ b/app/Http/Controllers/Category/IndexController.php @@ -32,6 +32,8 @@ use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Illuminate\View\View; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; /** * Class IndexController @@ -62,7 +64,11 @@ class IndexController extends Controller /** * Show all categories. * + * @param Request $request + * * @return Factory|View + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function index(Request $request) { diff --git a/app/Http/Controllers/Category/NoCategoryController.php b/app/Http/Controllers/Category/NoCategoryController.php index 11f6ade3a9..609f351a46 100644 --- a/app/Http/Controllers/Category/NoCategoryController.php +++ b/app/Http/Controllers/Category/NoCategoryController.php @@ -37,6 +37,8 @@ use Illuminate\Http\Request; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use Illuminate\View\View; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; /** * Class NoCategoryController @@ -69,9 +71,15 @@ class NoCategoryController extends Controller /** * Show transactions without a category. * + * @param Request $request + * @param Carbon|null $start + * @param Carbon|null $end + * * @return Factory|View * + * @throws ContainerExceptionInterface * @throws FireflyException + * @throws NotFoundExceptionInterface */ public function show(Request $request, ?Carbon $start = null, ?Carbon $end = null) { @@ -106,7 +114,11 @@ class NoCategoryController extends Controller /** * Show all transactions without a category. * + * @param Request $request + * * @return Factory|View + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function showAll(Request $request) { diff --git a/app/Http/Controllers/Category/ShowController.php b/app/Http/Controllers/Category/ShowController.php index d9a03f2f4a..c9ca74b93c 100644 --- a/app/Http/Controllers/Category/ShowController.php +++ b/app/Http/Controllers/Category/ShowController.php @@ -35,6 +35,8 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\Request; use Illuminate\Support\Collection; use Illuminate\View\View; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; /** * Class ShowController @@ -68,9 +70,16 @@ class ShowController extends Controller /** * Show a single category. * + * @param Request $request + * @param Category $category + * @param Carbon|null $start + * @param Carbon|null $end + * * @return Factory|View * + * @throws ContainerExceptionInterface * @throws FireflyException + * @throws NotFoundExceptionInterface */ public function show(Request $request, Category $category, ?Carbon $start = null, ?Carbon $end = null) { @@ -111,7 +120,12 @@ class ShowController extends Controller /** * Show all transactions within a category. * + * @param Request $request + * @param Category $category + * * @return Factory|View + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function showAll(Request $request, Category $category) { diff --git a/app/Http/Controllers/Chart/AccountController.php b/app/Http/Controllers/Chart/AccountController.php index 1e48b0d65f..632589163f 100644 --- a/app/Http/Controllers/Chart/AccountController.php +++ b/app/Http/Controllers/Chart/AccountController.php @@ -45,6 +45,7 @@ use Illuminate\Http\JsonResponse; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; +use Safe\Exceptions\JsonException; use function Safe\json_encode; /** @@ -482,7 +483,13 @@ class AccountController extends Controller /** * Shows overview of account during a single period. * + * @param Account $account + * @param Carbon $start + * @param Carbon $end + * + * @return JsonResponse * @throws FireflyException + * @throws JsonException */ public function period(Account $account, Carbon $start, Carbon $end): JsonResponse { @@ -571,7 +578,7 @@ class AccountController extends Controller $label = $current->isoFormat($format); $return[$key]['entries'][$label] = $amount; } - $current = app('navigation')->addPeriod($current, $step, 0); + $current = app('navigation')->addPeriod($current, $step); // here too, to fix #8041, the data is corrected to the end of the period. $current = app('navigation')->endOfX($current, $step, null); } diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index e22a6bd4d5..d79700c4a2 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -40,6 +40,8 @@ use FireflyIII\Support\Http\Controllers\ChartGeneration; use FireflyIII\Support\Http\Controllers\DateCalculation; use Illuminate\Http\JsonResponse; use Illuminate\Support\Collection; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; /** * Class CategoryController. @@ -66,7 +68,6 @@ class CategoryController extends Controller * Show an overview for a category for all time, per month/week/year. * TODO test method, for category refactor. * - * @throws FireflyException */ public function all(Category $category): JsonResponse { @@ -256,7 +257,12 @@ class CategoryController extends Controller * Chart for a specific period. * TODO test me, for category refactor. * - * @throws FireflyException + * @param Category $category + * @param Carbon $date + * + * @return JsonResponse + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function specificPeriod(Category $category, Carbon $date): JsonResponse { diff --git a/app/Http/Controllers/Chart/PiggyBankController.php b/app/Http/Controllers/Chart/PiggyBankController.php index 920c3a9301..1321de9eea 100644 --- a/app/Http/Controllers/Chart/PiggyBankController.php +++ b/app/Http/Controllers/Chart/PiggyBankController.php @@ -59,7 +59,6 @@ class PiggyBankController extends Controller * * TODO this chart is not multi currency aware. * - * @throws FireflyException */ public function history(PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank): JsonResponse { @@ -95,7 +94,7 @@ class PiggyBankController extends Controller $currentSum = $filtered->sum('amount'); $label = $oldest->isoFormat((string) trans('config.month_and_day_js', [], $locale)); $chartData[$label] = $currentSum; - $oldest = app('navigation')->addPeriod($oldest, $step, 0); + $oldest = app('navigation')->addPeriod($oldest, $step); } $finalFiltered = $set->filter( static fn (PiggyBankEvent $event) => $event->date->lte($today) diff --git a/app/Http/Controllers/Chart/ReportController.php b/app/Http/Controllers/Chart/ReportController.php index 9809c24140..4517b002fc 100644 --- a/app/Http/Controllers/Chart/ReportController.php +++ b/app/Http/Controllers/Chart/ReportController.php @@ -260,7 +260,7 @@ class ReportController extends Controller $expense['entries'][$title] = '0'; } - $currentStart = app('navigation')->addPeriod($currentStart, $preferredRange, 0); + $currentStart = app('navigation')->addPeriod($currentStart, $preferredRange); } Log::debug('End of sub-loop'); diff --git a/app/Http/Controllers/DebugController.php b/app/Http/Controllers/DebugController.php index 9138daf037..1ed00b4710 100644 --- a/app/Http/Controllers/DebugController.php +++ b/app/Http/Controllers/DebugController.php @@ -49,6 +49,7 @@ use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Route; use Illuminate\View\View; use Monolog\Handler\RotatingFileHandler; +use Safe\Exceptions\FilesystemException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use function Safe\file_get_contents; @@ -131,7 +132,7 @@ class DebugController extends Controller * * @return Factory|View * - * @throws FireflyException + * @throws FilesystemException */ public function index() { diff --git a/app/Http/Controllers/JavascriptController.php b/app/Http/Controllers/JavascriptController.php index bccff306d9..94696f0373 100644 --- a/app/Http/Controllers/JavascriptController.php +++ b/app/Http/Controllers/JavascriptController.php @@ -33,6 +33,8 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\Http\Controllers\GetConfigurationData; use Illuminate\Http\Request; use Illuminate\Http\Response; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; /** * Class JavascriptController. @@ -90,8 +92,14 @@ class JavascriptController extends Controller /** * Show some common variables to be used in scripts. * + * @param Request $request + * @param AccountRepositoryInterface $repository + * + * @return Response * @throws FireflyException - * */ + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ public function variables(Request $request, AccountRepositoryInterface $repository): Response { $account = $repository->find((int) $request->get('account')); diff --git a/app/Http/Controllers/Json/BoxController.php b/app/Http/Controllers/Json/BoxController.php index ce5f520180..cd845df963 100644 --- a/app/Http/Controllers/Json/BoxController.php +++ b/app/Http/Controllers/Json/BoxController.php @@ -52,7 +52,7 @@ class BoxController extends Controller #[Deprecated] public function available(): JsonResponse { - return response()->json([]); + return response()->json(); } /** diff --git a/app/Http/Controllers/Json/IntroController.php b/app/Http/Controllers/Json/IntroController.php index f3bd44b660..a08c32b6be 100644 --- a/app/Http/Controllers/Json/IntroController.php +++ b/app/Http/Controllers/Json/IntroController.php @@ -89,7 +89,6 @@ class IntroController extends Controller /** * Enable the boxes for a specific page again. * - * @throws FireflyException */ public function postEnable(string $route, ?string $specialPage = null): JsonResponse { @@ -109,7 +108,6 @@ class IntroController extends Controller /** * Set that you saw them. * - * @throws FireflyException */ public function postFinished(string $route, ?string $specialPage = null): JsonResponse { diff --git a/app/Http/Controllers/NewUserController.php b/app/Http/Controllers/NewUserController.php index 271c70e692..8350216064 100644 --- a/app/Http/Controllers/NewUserController.php +++ b/app/Http/Controllers/NewUserController.php @@ -87,7 +87,6 @@ class NewUserController extends Controller * * @return Redirector|RedirectResponse * - * @throws FireflyException */ public function submit(NewUserFormRequest $request, CurrencyRepositoryInterface $currencyRepository) { diff --git a/app/Http/Controllers/ObjectGroup/IndexController.php b/app/Http/Controllers/ObjectGroup/IndexController.php index 6cddb20792..91fc370f35 100644 --- a/app/Http/Controllers/ObjectGroup/IndexController.php +++ b/app/Http/Controllers/ObjectGroup/IndexController.php @@ -80,6 +80,6 @@ class IndexController extends Controller $newOrder = (int) $request->get('order'); $this->repository->setOrder($objectGroup, $newOrder); - return response()->json([]); + return response()->json(); } } diff --git a/app/Http/Controllers/PiggyBank/IndexController.php b/app/Http/Controllers/PiggyBank/IndexController.php index 64cab7c4ed..69bbd38780 100644 --- a/app/Http/Controllers/PiggyBank/IndexController.php +++ b/app/Http/Controllers/PiggyBank/IndexController.php @@ -78,7 +78,6 @@ class IndexController extends Controller * * @return Factory|View * - * @throws FireflyException */ public function index() { diff --git a/app/Http/Controllers/PreferencesController.php b/app/Http/Controllers/PreferencesController.php index d6f0435eed..7a22b91c06 100644 --- a/app/Http/Controllers/PreferencesController.php +++ b/app/Http/Controllers/PreferencesController.php @@ -43,6 +43,7 @@ use Illuminate\Routing\Redirector; use Illuminate\Support\Facades\Log; use Illuminate\View\View; +use Safe\Exceptions\FilesystemException; use function Safe\json_decode; use function Safe\file_get_contents; @@ -71,9 +72,12 @@ class PreferencesController extends Controller /** * Show overview of preferences. * + * @param AccountRepositoryInterface $repository + * * @return Factory|View * * @throws FireflyException + * @throws FilesystemException */ public function index(AccountRepositoryInterface $repository) { diff --git a/app/Http/Controllers/Profile/MfaController.php b/app/Http/Controllers/Profile/MfaController.php index 333801c380..69537bd12f 100644 --- a/app/Http/Controllers/Profile/MfaController.php +++ b/app/Http/Controllers/Profile/MfaController.php @@ -44,6 +44,8 @@ use Illuminate\Routing\Redirector; use Illuminate\Support\Facades\Log; use Illuminate\View\View; use PragmaRX\Recovery\Recovery; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; /** * Class MfaController @@ -84,7 +86,6 @@ class MfaController extends Controller } /** - * @throws FireflyException */ public function backupCodes(Request $request): Factory|RedirectResponse|View { @@ -230,9 +231,12 @@ class MfaController extends Controller /** * Submit 2FA for the first time. * + * @param TokenFormRequest $request + * * @return Redirector|RedirectResponse * - * @throws FireflyException + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function enableMFAPost(TokenFormRequest $request) { @@ -290,7 +294,10 @@ class MfaController extends Controller /** * TODO duplicate code. * - * @throws FireflyException + * @param string $mfaCode + * + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ private function addToMFAHistory(string $mfaCode): void { diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index b63ab5d86b..7eb2e745cf 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -48,6 +48,8 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; use Illuminate\View\View; use Laravel\Passport\ClientRepository; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; /** * Class ProfileController. @@ -136,7 +138,9 @@ class ProfileController extends Controller /** * Index for profile. * - * @throws FireflyException + * @return Factory|View + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function index(): Factory|View { diff --git a/app/Http/Controllers/Recurring/IndexController.php b/app/Http/Controllers/Recurring/IndexController.php index d6c7e41d7f..9923a42d0a 100644 --- a/app/Http/Controllers/Recurring/IndexController.php +++ b/app/Http/Controllers/Recurring/IndexController.php @@ -36,6 +36,8 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\View\View; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; use Symfony\Component\HttpFoundation\ParameterBag; /** @@ -71,9 +73,13 @@ class IndexController extends Controller * TODO the notes of a recurrence are pretty pointless at this moment. * Show all recurring transactions. * + * @param Request $request + * * @return Factory|View * * @throws FireflyException + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function index(Request $request) { diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index 017aa4d9fe..126d312f71 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -39,6 +39,8 @@ use Illuminate\Http\RedirectResponse; use Illuminate\Routing\Redirector; use Illuminate\Support\Collection; use Illuminate\View\View; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; /** * Class ReportController. @@ -225,7 +227,11 @@ class ReportController extends Controller /** * Show index. * + * @param AccountRepositoryInterface $repository + * * @return Factory|View + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function index(AccountRepositoryInterface $repository) { diff --git a/app/Http/Controllers/Rule/IndexController.php b/app/Http/Controllers/Rule/IndexController.php index 5d53c38e9c..7273a72e56 100644 --- a/app/Http/Controllers/Rule/IndexController.php +++ b/app/Http/Controllers/Rule/IndexController.php @@ -82,7 +82,7 @@ class IndexController extends Controller $order = (int) $request->get('order'); $this->ruleRepos->moveRule($rule, $ruleGroup, $order); - return response()->json([]); + return response()->json(); } public function search(Rule $rule): RedirectResponse diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index f69cd62c24..0fd2ef0336 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -37,6 +37,8 @@ use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; use Illuminate\View\View; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; /** * Class TagController. @@ -218,9 +220,16 @@ class TagController extends Controller /** * Show a single tag. * + * @param Request $request + * @param Tag $tag + * @param Carbon|null $start + * @param Carbon|null $end + * * @return Factory|View * + * @throws ContainerExceptionInterface * @throws FireflyException + * @throws NotFoundExceptionInterface */ public function show(Request $request, Tag $tag, ?Carbon $start = null, ?Carbon $end = null) { @@ -261,7 +270,12 @@ class TagController extends Controller /** * Show a single tag over all time. * + * @param Request $request + * @param Tag $tag + * * @return Factory|View + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function showAll(Request $request, Tag $tag) { diff --git a/app/Http/Controllers/Transaction/CreateController.php b/app/Http/Controllers/Transaction/CreateController.php index c69ddfc2ce..1fc030e5e9 100644 --- a/app/Http/Controllers/Transaction/CreateController.php +++ b/app/Http/Controllers/Transaction/CreateController.php @@ -37,6 +37,9 @@ use Illuminate\Contracts\View\View; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; +use Safe\Exceptions\UrlException; use function Safe\parse_url; /** @@ -98,10 +101,14 @@ class CreateController extends Controller /** * Create a new transaction group. * + * @param string|null $objectType + * * @return Factory|View * - * @throws FireflyException - * */ + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws UrlException + */ public function create(?string $objectType) { Preferences::mark(); diff --git a/app/Http/Controllers/Transaction/EditController.php b/app/Http/Controllers/Transaction/EditController.php index 5b30ec7a9f..2fa41b6de9 100644 --- a/app/Http/Controllers/Transaction/EditController.php +++ b/app/Http/Controllers/Transaction/EditController.php @@ -35,6 +35,9 @@ use Illuminate\Http\RedirectResponse; use Illuminate\Routing\Redirector; use Illuminate\View\View; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; +use Safe\Exceptions\UrlException; use function Safe\parse_url; /** @@ -65,7 +68,12 @@ class EditController extends Controller } /** + * @param TransactionGroup $transactionGroup + * * @return Factory|Redirector|RedirectResponse|View + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws UrlException */ public function edit(TransactionGroup $transactionGroup) { diff --git a/app/Http/Controllers/Transaction/IndexController.php b/app/Http/Controllers/Transaction/IndexController.php index 54e6a6e967..92f67137c6 100644 --- a/app/Http/Controllers/Transaction/IndexController.php +++ b/app/Http/Controllers/Transaction/IndexController.php @@ -34,6 +34,8 @@ use FireflyIII\Support\Http\Controllers\PeriodOverview; use Illuminate\Contracts\View\Factory; use Illuminate\Http\Request; use Illuminate\View\View; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; /** * Class IndexController @@ -67,9 +69,16 @@ class IndexController extends Controller /** * Index for a range of transactions. * + * @param Request $request + * @param string $objectType + * @param Carbon|null $start + * @param Carbon|null $end + * * @return Factory|View * + * @throws ContainerExceptionInterface * @throws FireflyException + * @throws NotFoundExceptionInterface */ public function index(Request $request, string $objectType, ?Carbon $start = null, ?Carbon $end = null) { @@ -123,7 +132,12 @@ class IndexController extends Controller /** * Index for ALL transactions. * + * @param Request $request + * @param string $objectType + * * @return Factory|View + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function indexAll(Request $request, string $objectType) { diff --git a/app/Http/Controllers/TransactionCurrency/DeleteController.php b/app/Http/Controllers/TransactionCurrency/DeleteController.php index c7cc7fb83a..ca25c9c2e5 100644 --- a/app/Http/Controllers/TransactionCurrency/DeleteController.php +++ b/app/Http/Controllers/TransactionCurrency/DeleteController.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\TransactionCurrency; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; @@ -66,7 +67,11 @@ class DeleteController extends Controller /** * Deletes a currency. * + * @param Request $request + * @param TransactionCurrency $currency + * * @return Factory|Redirector|RedirectResponse|View + * @throws FireflyException */ public function delete(Request $request, TransactionCurrency $currency) { @@ -99,7 +104,11 @@ class DeleteController extends Controller /** * Destroys a currency. * + * @param Request $request + * @param TransactionCurrency $currency + * * @return Redirector|RedirectResponse + * @throws FireflyException */ public function destroy(Request $request, TransactionCurrency $currency) { diff --git a/app/Http/Controllers/TransactionCurrency/EditController.php b/app/Http/Controllers/TransactionCurrency/EditController.php index 926e973f44..092b5c623f 100644 --- a/app/Http/Controllers/TransactionCurrency/EditController.php +++ b/app/Http/Controllers/TransactionCurrency/EditController.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\TransactionCurrency; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\CurrencyFormRequest; use FireflyIII\Models\TransactionCurrency; @@ -106,7 +107,11 @@ class EditController extends Controller /** * Updates a currency. * + * @param CurrencyFormRequest $request + * @param TransactionCurrency $currency + * * @return Redirector|RedirectResponse + * @throws FireflyException */ public function update(CurrencyFormRequest $request, TransactionCurrency $currency) { diff --git a/app/Http/Controllers/TransactionCurrency/IndexController.php b/app/Http/Controllers/TransactionCurrency/IndexController.php index d63ffe5897..62566bbd3b 100644 --- a/app/Http/Controllers/TransactionCurrency/IndexController.php +++ b/app/Http/Controllers/TransactionCurrency/IndexController.php @@ -33,6 +33,8 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\View\View; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; class IndexController extends Controller { @@ -61,7 +63,11 @@ class IndexController extends Controller /** * Show overview of currencies. * + * @param Request $request + * * @return Factory|View + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function index(Request $request) { diff --git a/app/Http/Middleware/AcceptHeaders.php b/app/Http/Middleware/AcceptHeaders.php index 0f797dea01..d8884aebf3 100644 --- a/app/Http/Middleware/AcceptHeaders.php +++ b/app/Http/Middleware/AcceptHeaders.php @@ -27,6 +27,7 @@ namespace FireflyIII\Http\Middleware; use FireflyIII\Exceptions\BadHttpHeaderException; use Illuminate\Http\Request; use Illuminate\Http\Response; +use Safe\Exceptions\PcreException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use function Safe\preg_match; @@ -36,9 +37,13 @@ class AcceptHeaders /** * Handle the incoming requests. * + * @param Request $request + * @param callable $next + * * @return Response * * @throws BadHttpHeaderException + * @throws PcreException */ public function handle(Request $request, callable $next): mixed { diff --git a/app/Http/Requests/ReportFormRequest.php b/app/Http/Requests/ReportFormRequest.php index 7a4fcd3cf9..0061ca310a 100644 --- a/app/Http/Requests/ReportFormRequest.php +++ b/app/Http/Requests/ReportFormRequest.php @@ -36,6 +36,7 @@ use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; +use Safe\Exceptions\PcreException; use function Safe\preg_match; /** @@ -133,7 +134,9 @@ class ReportFormRequest extends FormRequest /** * Validate end date. * + * @return Carbon * @throws FireflyException + * @throws PcreException */ public function getEndDate(): Carbon { @@ -171,7 +174,9 @@ class ReportFormRequest extends FormRequest /** * Validate start date. * + * @return Carbon * @throws FireflyException + * @throws PcreException */ public function getStartDate(): Carbon { diff --git a/app/Jobs/DownloadExchangeRates.php b/app/Jobs/DownloadExchangeRates.php index 8d22837468..1bbb3ee10e 100644 --- a/app/Jobs/DownloadExchangeRates.php +++ b/app/Jobs/DownloadExchangeRates.php @@ -41,6 +41,7 @@ use Illuminate\Queue\SerializesModels; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; +use Safe\Exceptions\JsonException; use function Safe\json_decode; /** @@ -94,7 +95,10 @@ class DownloadExchangeRates implements ShouldQueue } /** + * @param TransactionCurrency $currency + * * @throws GuzzleException + * @throws JsonException */ private function downloadRates(TransactionCurrency $currency): void { diff --git a/app/Mail/ReportNewJournalsMail.php b/app/Mail/ReportNewJournalsMail.php index e0a492fa47..0fd9ac7127 100644 --- a/app/Mail/ReportNewJournalsMail.php +++ b/app/Mail/ReportNewJournalsMail.php @@ -52,6 +52,7 @@ class ReportNewJournalsMail extends Mailable * Build the message. * * @return $this + * @throws FireflyException */ public function build(): self { diff --git a/app/Repositories/Account/AccountTasker.php b/app/Repositories/Account/AccountTasker.php index 1b3b7ff1ee..a9ca59cb65 100644 --- a/app/Repositories/Account/AccountTasker.php +++ b/app/Repositories/Account/AccountTasker.php @@ -144,7 +144,6 @@ class AccountTasker implements AccountTaskerInterface, UserGroupInterface } /** - * @throws FireflyException */ private function groupExpenseByDestination(array $array): array { @@ -232,7 +231,6 @@ class AccountTasker implements AccountTaskerInterface, UserGroupInterface } /** - * @throws FireflyException */ private function groupIncomeBySource(array $array): array { diff --git a/app/Repositories/Budget/AvailableBudgetRepository.php b/app/Repositories/Budget/AvailableBudgetRepository.php index 177900d154..ee9db0ca62 100644 --- a/app/Repositories/Budget/AvailableBudgetRepository.php +++ b/app/Repositories/Budget/AvailableBudgetRepository.php @@ -113,6 +113,7 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface, U ; } + #[\Deprecated] public function getAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end): string { $amount = '0'; diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 8aaaa40b8a..23cc13e273 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -377,9 +377,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface return; } - if (null !== $dbNote) { - $dbNote->delete(); - } + $dbNote?->delete(); } public function getAutoBudget(Budget $budget): ?AutoBudget @@ -389,7 +387,6 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface } /** - * @throws FireflyException */ private function updateAutoBudget(Budget $budget, array $data): void { @@ -527,11 +524,8 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface public function firstUseDate(Budget $budget): ?Carbon { $journal = $budget->transactionJournals()->orderBy('date', 'ASC')->first(); - if (null !== $journal) { - return $journal->date; - } + return $journal?->date; - return null; } public function getAttachments(Budget $budget): Collection @@ -570,11 +564,8 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface public function getNoteText(Budget $budget): ?string { $note = $budget->notes()->first(); - if (null === $note) { - return null; - } + return $note?->text; - return $note->text; } public function searchBudget(string $query, int $limit): Collection diff --git a/app/Repositories/Budget/NoBudgetRepository.php b/app/Repositories/Budget/NoBudgetRepository.php index f48176ab9a..8ae96cb192 100644 --- a/app/Repositories/Budget/NoBudgetRepository.php +++ b/app/Repositories/Budget/NoBudgetRepository.php @@ -41,6 +41,7 @@ class NoBudgetRepository implements NoBudgetRepositoryInterface, UserGroupInterf { use UserGroupTrait; + #[\Deprecated] public function getNoBudgetPeriodReport(Collection $accounts, Carbon $start, Carbon $end): array { $carbonFormat = app('navigation')->preferredCarbonFormat($start, $end); diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 894690e9e6..6856e3552a 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -213,11 +213,8 @@ class CategoryRepository implements CategoryRepositoryInterface, UserGroupInterf $query = $category->transactionJournals()->orderBy('date', 'ASC'); $result = $query->first(['transaction_journals.*']); - if (null !== $result) { - return $result->date; - } + return $result?->date; - return null; } private function getFirstTransactionDate(Category $category): ?Carbon @@ -264,11 +261,8 @@ class CategoryRepository implements CategoryRepositoryInterface, UserGroupInterf public function getNoteText(Category $category): ?string { $dbNote = $category->notes()->first(); - if (null === $dbNote) { - return null; - } + return $dbNote?->text; - return $dbNote->text; } /** @@ -307,11 +301,8 @@ class CategoryRepository implements CategoryRepositoryInterface, UserGroupInterf $result = $query->first(['transaction_journals.*']); - if (null !== $result) { - return $result->date; - } + return $result?->date; - return null; } /** diff --git a/app/Repositories/Currency/CurrencyRepository.php b/app/Repositories/Currency/CurrencyRepository.php index cb5e24858b..c76b5badfb 100644 --- a/app/Repositories/Currency/CurrencyRepository.php +++ b/app/Repositories/Currency/CurrencyRepository.php @@ -45,6 +45,7 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use Override; +use Safe\Exceptions\JsonException; use function Safe\json_encode; /** @@ -65,7 +66,10 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf } /** - * @throws FireflyException + * @param TransactionCurrency $currency + * + * @return string|null + * @throws JsonException */ public function currencyInUseAt(TransactionCurrency $currency): ?string { @@ -234,7 +238,6 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf /** * Find by object, ID or code. Returns user default or system default. * - * @throws FireflyException */ public function findCurrency(?int $currencyId, ?string $currencyCode): TransactionCurrency { diff --git a/app/Repositories/Journal/JournalCLIRepository.php b/app/Repositories/Journal/JournalCLIRepository.php index ea923bfffc..dd47a39323 100644 --- a/app/Repositories/Journal/JournalCLIRepository.php +++ b/app/Repositories/Journal/JournalCLIRepository.php @@ -166,11 +166,8 @@ class JournalCLIRepository implements JournalCLIRepositoryInterface, UserGroupIn public function getNoteText(TransactionJournal $journal): ?string { $note = $journal->notes()->first(); - if (null === $note) { - return null; - } + return $note?->text; - return $note->text; } /** diff --git a/app/Repositories/Recurring/RecurringRepository.php b/app/Repositories/Recurring/RecurringRepository.php index c8ee7b89c1..f131b3d1f6 100644 --- a/app/Repositories/Recurring/RecurringRepository.php +++ b/app/Repositories/Recurring/RecurringRepository.php @@ -431,7 +431,6 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte /** * Parse the repetition in a string that is user readable. * - * @throws FireflyException */ public function repetitionDescription(RecurrenceRepetition $repetition): string { diff --git a/app/Repositories/TransactionGroup/TransactionGroupRepository.php b/app/Repositories/TransactionGroup/TransactionGroupRepository.php index c8c03b7a38..78acf6d253 100644 --- a/app/Repositories/TransactionGroup/TransactionGroupRepository.php +++ b/app/Repositories/TransactionGroup/TransactionGroupRepository.php @@ -177,11 +177,8 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface, ->where('noteable_type', TransactionJournal::class) ->first() ; - if (null === $note) { - return null; - } + return $note?->text; - return $note->text; } /** diff --git a/app/Repositories/User/UserRepository.php b/app/Repositories/User/UserRepository.php index e6dbb7ec52..e00b15ba9d 100644 --- a/app/Repositories/User/UserRepository.php +++ b/app/Repositories/User/UserRepository.php @@ -161,11 +161,8 @@ class UserRepository implements UserRepositoryInterface { /** @var null|Role $role */ $role = $user->roles()->first(); - if (null !== $role) { - return $role->name; - } + return $role?->name; - return null; } /** @@ -379,7 +376,6 @@ class UserRepository implements UserRepositoryInterface * This updates the users email address. Same as changeEmail just without most logging. This makes sure that the * undo/confirm routine can't catch this one. The user is NOT blocked. * - * @throws FireflyException * * @see changeEmail */ diff --git a/app/Repositories/UserGroup/UserGroupRepository.php b/app/Repositories/UserGroup/UserGroupRepository.php index 3f9bcdace1..691807110c 100644 --- a/app/Repositories/UserGroup/UserGroupRepository.php +++ b/app/Repositories/UserGroup/UserGroupRepository.php @@ -97,7 +97,6 @@ class UserGroupRepository implements UserGroupRepositoryInterface, UserGroupInte /** * Returns all groups the user is member in. * - * {@inheritDoc} */ public function get(): Collection { @@ -137,7 +136,6 @@ class UserGroupRepository implements UserGroupRepositoryInterface, UserGroupInte if (!$existingGroup instanceof UserGroup) { $exists = false; - /** @var UserGroup $existingGroup */ $existingGroup = $this->store(['user' => $user, 'title' => $groupName]); } $groupName = sprintf('%s-%s', $user->email, substr(sha1(random_int(1000, 9999).microtime()), 0, 4)); @@ -168,7 +166,6 @@ class UserGroupRepository implements UserGroupRepositoryInterface, UserGroupInte /** * Returns all groups. * - * {@inheritDoc} */ public function getAll(): Collection { diff --git a/app/Rules/Account/IsValidAccountType.php b/app/Rules/Account/IsValidAccountType.php index 52042572f1..d2287ff702 100644 --- a/app/Rules/Account/IsValidAccountType.php +++ b/app/Rules/Account/IsValidAccountType.php @@ -46,7 +46,6 @@ class IsValidAccountType implements ValidationRule $filtered = []; $keys = array_keys($this->types); - /** @var mixed $entry */ foreach ($value as $entry) { $entry = (string) $entry; if (!in_array($entry, $keys, true)) { diff --git a/app/Services/Internal/Support/AccountServiceTrait.php b/app/Services/Internal/Support/AccountServiceTrait.php index f3225155c7..5525181b97 100644 --- a/app/Services/Internal/Support/AccountServiceTrait.php +++ b/app/Services/Internal/Support/AccountServiceTrait.php @@ -155,9 +155,7 @@ trait AccountServiceTrait { $dbNote = $account->notes()->first(); if ('' === $note) { - if (null !== $dbNote) { - $dbNote->delete(); - } + $dbNote?->delete(); return true; } @@ -342,7 +340,6 @@ trait AccountServiceTrait } /** - * @throws FireflyException */ protected function getCurrency(int $currencyId, string $currencyCode): TransactionCurrency { @@ -377,7 +374,7 @@ trait AccountServiceTrait throw new FireflyException('Amount for update liability credit/debit was unexpectedly 0.'); } - // if direction is "debit" (i owe this debt), amount is negative. + // if direction is "debit" (I owe this debt), amount is negative. // which means the liability will have a negative balance which the user must fill. $openingBalance = app('steam')->negative($openingBalance); diff --git a/app/Services/Internal/Support/BillServiceTrait.php b/app/Services/Internal/Support/BillServiceTrait.php index c3a73b1e83..a9217f347d 100644 --- a/app/Services/Internal/Support/BillServiceTrait.php +++ b/app/Services/Internal/Support/BillServiceTrait.php @@ -56,9 +56,7 @@ trait BillServiceTrait { if ('' === $note) { $dbNote = $bill->notes()->first(); - if (null !== $dbNote) { - $dbNote->delete(); - } + $dbNote?->delete(); return true; } diff --git a/app/Services/Internal/Support/JournalServiceTrait.php b/app/Services/Internal/Support/JournalServiceTrait.php index 8cd15203ca..abe783365f 100644 --- a/app/Services/Internal/Support/JournalServiceTrait.php +++ b/app/Services/Internal/Support/JournalServiceTrait.php @@ -39,6 +39,7 @@ use FireflyIII\Rules\UniqueIban; use FireflyIII\Support\NullArrayObject; use Illuminate\Support\Facades\Log; +use Safe\Exceptions\JsonException; use function Safe\json_encode; /** @@ -265,7 +266,13 @@ trait JournalServiceTrait } /** + * @param Account|null $account + * @param array $data + * @param string $preferredType + * + * @return Account|null * @throws FireflyException + * @throws JsonException */ private function createAccount(?Account $account, array $data, string $preferredType): ?Account { diff --git a/app/Services/Internal/Support/RecurringTransactionTrait.php b/app/Services/Internal/Support/RecurringTransactionTrait.php index 6ac399f6e5..ebb7aad99f 100644 --- a/app/Services/Internal/Support/RecurringTransactionTrait.php +++ b/app/Services/Internal/Support/RecurringTransactionTrait.php @@ -54,9 +54,7 @@ trait RecurringTransactionTrait { if ('' === $note) { $dbNote = $recurrence->notes()->first(); - if (null !== $dbNote) { - $dbNote->delete(); - } + $dbNote?->delete(); return true; } diff --git a/app/Services/Internal/Update/AccountUpdateService.php b/app/Services/Internal/Update/AccountUpdateService.php index 5e1f230b4d..341e72b33a 100644 --- a/app/Services/Internal/Update/AccountUpdateService.php +++ b/app/Services/Internal/Update/AccountUpdateService.php @@ -285,7 +285,6 @@ class AccountUpdateService } /** - * @throws FireflyException */ private function updatePreferences(Account $account): void { diff --git a/app/Services/Internal/Update/BillUpdateService.php b/app/Services/Internal/Update/BillUpdateService.php index a4aca38ce4..d71d82a524 100644 --- a/app/Services/Internal/Update/BillUpdateService.php +++ b/app/Services/Internal/Update/BillUpdateService.php @@ -47,7 +47,6 @@ class BillUpdateService protected User $user; /** - * @throws FireflyException */ public function update(Bill $bill, array $data): Bill { diff --git a/app/Services/Internal/Update/CategoryUpdateService.php b/app/Services/Internal/Update/CategoryUpdateService.php index 28b5f36f00..193a5957b2 100644 --- a/app/Services/Internal/Update/CategoryUpdateService.php +++ b/app/Services/Internal/Update/CategoryUpdateService.php @@ -139,9 +139,7 @@ class CategoryUpdateService } if ('' === $note) { $dbNote = $category->notes()->first(); - if (null !== $dbNote) { - $dbNote->delete(); - } + $dbNote?->delete(); return; } diff --git a/app/Support/Binder/CLIToken.php b/app/Support/Binder/CLIToken.php index d55e5dceca..b57ae19261 100644 --- a/app/Support/Binder/CLIToken.php +++ b/app/Support/Binder/CLIToken.php @@ -37,7 +37,6 @@ class CLIToken implements BinderInterface /** * @return mixed * - * @throws FireflyException */ public static function routeBinder(string $value, Route $route) { diff --git a/app/Support/Chart/Category/WholePeriodChartGenerator.php b/app/Support/Chart/Category/WholePeriodChartGenerator.php index ce43b1d16e..27d6026c2f 100644 --- a/app/Support/Chart/Category/WholePeriodChartGenerator.php +++ b/app/Support/Chart/Category/WholePeriodChartGenerator.php @@ -62,7 +62,7 @@ class WholePeriodChartGenerator $currentEnd = app('navigation')->endOfPeriod($current, $step); $spent[$key] = $opsRepository->sumExpenses($current, $currentEnd, $accounts, $collection); $earned[$key] = $opsRepository->sumIncome($current, $currentEnd, $accounts, $collection); - $current = app('navigation')->addPeriod($current, $step, 0); + $current = app('navigation')->addPeriod($current, $step); } $currencies = $this->extractCurrencies($spent) + $this->extractCurrencies($earned); @@ -104,7 +104,7 @@ class WholePeriodChartGenerator $chartData[$spentInfoKey]['entries'][$label] = app('steam')->bcround($spentAmount, $currency['currency_decimal_places']); $chartData[$earnedInfoKey]['entries'][$label] = app('steam')->bcround($earnedAmount, $currency['currency_decimal_places']); } - $current = app('navigation')->addPeriod($current, $step, 0); + $current = app('navigation')->addPeriod($current, $step); } return $chartData; diff --git a/app/Support/Export/ExportDataGenerator.php b/app/Support/Export/ExportDataGenerator.php index 926f371108..d1b19bf7e4 100644 --- a/app/Support/Export/ExportDataGenerator.php +++ b/app/Support/Export/ExportDataGenerator.php @@ -62,6 +62,8 @@ use Illuminate\Support\Facades\Log; use League\Csv\CannotInsertRecord; use League\Csv\Exception; use League\Csv\Writer; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; /** * Class ExportDataGenerator @@ -105,7 +107,12 @@ class ExportDataGenerator } /** + * @return array + * @throws CannotInsertRecord + * @throws ContainerExceptionInterface + * @throws Exception * @throws FireflyException + * @throws NotFoundExceptionInterface */ public function export(): array { @@ -747,9 +754,12 @@ class ExportDataGenerator } /** + * @return string * @throws CannotInsertRecord * @throws Exception * @throws FireflyException + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ private function exportTags(): string { diff --git a/app/Support/Form/CurrencyForm.php b/app/Support/Form/CurrencyForm.php index bf748c6097..5bf2e0ba77 100644 --- a/app/Support/Form/CurrencyForm.php +++ b/app/Support/Form/CurrencyForm.php @@ -165,8 +165,6 @@ class CurrencyForm } /** - * @phpstan-param view-string $view - * * @throws FireflyException */ protected function currencyField(string $name, string $view, mixed $value = null, ?array $options = null): string diff --git a/app/Support/Http/Api/ExchangeRateConverter.php b/app/Support/Http/Api/ExchangeRateConverter.php index d92313907a..6f6728e825 100644 --- a/app/Support/Http/Api/ExchangeRateConverter.php +++ b/app/Support/Http/Api/ExchangeRateConverter.php @@ -243,7 +243,7 @@ class ExchangeRateConverter private function getRate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string { $key = $this->getCacheKey($from, $to, $date); - $res = Cache::get($key, null); + $res = Cache::get($key); // find in cache if (null !== $res) { diff --git a/app/Support/Http/Controllers/AugumentData.php b/app/Support/Http/Controllers/AugumentData.php index 2046b7e5b2..d7422f34c2 100644 --- a/app/Support/Http/Controllers/AugumentData.php +++ b/app/Support/Http/Controllers/AugumentData.php @@ -219,7 +219,7 @@ trait AugumentData $entry->pc_spent = $spent; // normal amount: - $expenses = $opsRepository->sumExpenses($currentStart, $currentEnd, null, $budgetCollection, $entry->transactionCurrency, false); + $expenses = $opsRepository->sumExpenses($currentStart, $currentEnd, null, $budgetCollection, $entry->transactionCurrency); $spent = $expenses[$entry->transactionCurrency->id]['sum'] ?? '0'; $entry->spent = $spent; diff --git a/app/Support/Http/Controllers/GetConfigurationData.php b/app/Support/Http/Controllers/GetConfigurationData.php index b0bb16b3f1..097204bd83 100644 --- a/app/Support/Http/Controllers/GetConfigurationData.php +++ b/app/Support/Http/Controllers/GetConfigurationData.php @@ -118,7 +118,7 @@ trait GetConfigurationData $previousEnd = app('navigation')->endOfPeriod($previousStart, $viewRange); $ranges[$index] = [$previousStart, $previousEnd]; - $nextDate = app('navigation')->addPeriod($start, $viewRange, 0); + $nextDate = app('navigation')->addPeriod($start, $viewRange); $index = app('navigation')->periodShow($nextDate, $viewRange); $nextStart = app('navigation')->startOfPeriod($nextDate, $viewRange); $nextEnd = app('navigation')->endOfPeriod($nextStart, $viewRange); diff --git a/app/Support/JsonApi/Enrichments/AvailableBudgetEnrichment.php b/app/Support/JsonApi/Enrichments/AvailableBudgetEnrichment.php index a6a8823368..b794275706 100644 --- a/app/Support/JsonApi/Enrichments/AvailableBudgetEnrichment.php +++ b/app/Support/JsonApi/Enrichments/AvailableBudgetEnrichment.php @@ -153,8 +153,8 @@ class AvailableBudgetEnrichment implements EnrichmentInterface $id = (int)$availableBudget->id; $currencyId = $this->currencyIds[$id]; $currency = $this->currencies[$currencyId]; - $filteredSpentInBudgets = $this->opsRepository->sumCollectedExpenses($spentInBudgets, $availableBudget->start_date, $availableBudget->end_date, $currency, false); - $filteredSpentOutsideBudgets = $this->opsRepository->sumCollectedExpenses($spentOutsideBudgets, $availableBudget->start_date, $availableBudget->end_date, $currency, false); + $filteredSpentInBudgets = $this->opsRepository->sumCollectedExpenses($spentInBudgets, $availableBudget->start_date, $availableBudget->end_date, $currency); + $filteredSpentOutsideBudgets = $this->opsRepository->sumCollectedExpenses($spentOutsideBudgets, $availableBudget->start_date, $availableBudget->end_date, $currency); $this->spentInBudgets[$id] = array_values($filteredSpentInBudgets); $this->spentOutsideBudgets[$id] = array_values($filteredSpentOutsideBudgets); diff --git a/app/Support/JsonApi/Enrichments/BudgetEnrichment.php b/app/Support/JsonApi/Enrichments/BudgetEnrichment.php index cb875aca78..8d548405d2 100644 --- a/app/Support/JsonApi/Enrichments/BudgetEnrichment.php +++ b/app/Support/JsonApi/Enrichments/BudgetEnrichment.php @@ -156,10 +156,10 @@ class BudgetEnrichment implements EnrichmentInterface $opsRepository->setUserGroup($this->userGroup); // $spent = $this->beautify(); // $set = $this->opsRepository->sumExpenses($start, $end, null, new Collection()->push($budget)) - $expenses = $opsRepository->collectExpenses($this->start, $this->end, null, $this->collection, null); + $expenses = $opsRepository->collectExpenses($this->start, $this->end, null, $this->collection); foreach ($this->collection as $item) { $id = (int)$item->id; - $this->spent[$id] = array_values($opsRepository->sumCollectedExpensesByBudget($expenses, $item, false)); + $this->spent[$id] = array_values($opsRepository->sumCollectedExpensesByBudget($expenses, $item)); $this->pcSpent[$id] = array_values($opsRepository->sumCollectedExpensesByBudget($expenses, $item, true)); } } diff --git a/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php b/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php index db1e248708..0df7aea164 100644 --- a/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php +++ b/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php @@ -41,7 +41,7 @@ use Illuminate\Support\Facades\Log; class BudgetLimitEnrichment implements EnrichmentInterface { private Collection $collection; - private bool $convertToPrimary = true; // @phpstan-ignore-line + private bool $convertToPrimary; // @phpstan-ignore-line private array $currencies = []; private array $currencyIds = []; private Carbon $end; @@ -120,14 +120,14 @@ class BudgetLimitEnrichment implements EnrichmentInterface $repository = app(OperationsRepository::class); $repository->setUser($this->user); - $expenses = $repository->collectExpenses($this->start, $this->end, null, $budgets, null); + $expenses = $repository->collectExpenses($this->start, $this->end, null, $budgets); /** @var BudgetLimit $budgetLimit */ foreach ($this->collection as $budgetLimit) { Log::debug(sprintf('Filtering expenses for budget limit #%d (budget #%d)', $budgetLimit->id, $budgetLimit->budget_id)); $id = (int)$budgetLimit->id; $filteredExpenses = $this->filterToBudget($expenses, $budgetLimit->budget_id); - $filteredExpenses = $repository->sumCollectedExpenses($filteredExpenses, $budgetLimit->start_date, $budgetLimit->end_date, $budgetLimit->transactionCurrency, false); + $filteredExpenses = $repository->sumCollectedExpenses($filteredExpenses, $budgetLimit->start_date, $budgetLimit->end_date, $budgetLimit->transactionCurrency); $this->expenses[$id] = array_values($filteredExpenses); if (true === $this->convertToPrimary && $budgetLimit->transactionCurrency->id !== $this->primaryCurrency->id) { diff --git a/app/Support/JsonApi/Enrichments/CategoryEnrichment.php b/app/Support/JsonApi/Enrichments/CategoryEnrichment.php index b5ddd22c52..f470e1c111 100644 --- a/app/Support/JsonApi/Enrichments/CategoryEnrichment.php +++ b/app/Support/JsonApi/Enrichments/CategoryEnrichment.php @@ -145,11 +145,11 @@ class CategoryEnrichment implements EnrichmentInterface $transfers = $opsRepository->collectTransfers($this->start, $this->end, null, $this->collection); foreach ($this->collection as $item) { $id = (int)$item->id; - $this->spent[$id] = array_values($opsRepository->sumCollectedTransactionsByCategory($expenses, $item, 'negative', false)); + $this->spent[$id] = array_values($opsRepository->sumCollectedTransactionsByCategory($expenses, $item, 'negative')); $this->pcSpent[$id] = array_values($opsRepository->sumCollectedTransactionsByCategory($expenses, $item, 'negative', true)); - $this->earned[$id] = array_values($opsRepository->sumCollectedTransactionsByCategory($income, $item, 'positive', false)); + $this->earned[$id] = array_values($opsRepository->sumCollectedTransactionsByCategory($income, $item, 'positive')); $this->pcEarned[$id] = array_values($opsRepository->sumCollectedTransactionsByCategory($income, $item, 'positive', true)); - $this->transfers[$id] = array_values($opsRepository->sumCollectedTransactionsByCategory($transfers, $item, 'positive', false)); + $this->transfers[$id] = array_values($opsRepository->sumCollectedTransactionsByCategory($transfers, $item, 'positive')); $this->pcTransfers[$id] = array_values($opsRepository->sumCollectedTransactionsByCategory($transfers, $item, 'positive', true)); } } diff --git a/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php b/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php index f1a79ad0fc..e51c110078 100644 --- a/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php +++ b/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php @@ -159,7 +159,9 @@ class PiggyBankEnrichment implements EnrichmentInterface // get suggested per month. $meta['save_per_month'] = Steam::bcround($this->getSuggestedMonthlyAmount($this->date, $item->target_date, $meta['target_amount'], $meta['current_amount']), $currency->decimal_places); - $meta['pc_save_per_month'] = Steam::bcround($this->getSuggestedMonthlyAmount($this->date, $item->target_date, $meta['pc_target_amount'], $meta['pc_current_amount']), $currency->decimal_places); + if(null !== $meta['pc_current_amount']) { + $meta['pc_save_per_month'] = Steam::bcround($this->getSuggestedMonthlyAmount($this->date, $item->target_date, $meta['pc_target_amount'], $meta['pc_current_amount']), $currency->decimal_places); + } $item->meta = $meta; diff --git a/app/Support/JsonApi/Enrichments/RecurringEnrichment.php b/app/Support/JsonApi/Enrichments/RecurringEnrichment.php index 7fdfc1c5dc..1ea71f1dc4 100644 --- a/app/Support/JsonApi/Enrichments/RecurringEnrichment.php +++ b/app/Support/JsonApi/Enrichments/RecurringEnrichment.php @@ -60,8 +60,8 @@ class RecurringEnrichment implements EnrichmentInterface private Collection $collection; // private array $transactionTypeIds = []; // private array $transactionTypes = []; - private bool $convertToPrimary = false; - private array $currencies = []; + private bool $convertToPrimary; + private array $currencies = []; private array $currencyIds = []; private array $destinationAccountIds = []; private array $foreignCurrencyIds = []; diff --git a/app/Support/Models/BillDateCalculator.php b/app/Support/Models/BillDateCalculator.php index 3d49c867a3..6b7cbd2243 100644 --- a/app/Support/Models/BillDateCalculator.php +++ b/app/Support/Models/BillDateCalculator.php @@ -98,7 +98,7 @@ class BillDateCalculator $diffEOM = $daysUntilEOM - $nextUntilEOM; if ($diffEOM > 0) { Log::debug(sprintf('Bill start is %d days from the end of the month. nextExceptedMatch is %d days from the end of the month.', $daysUntilEOM, $nextUntilEOM)); - $nextExpectedMatch->subDays(1); + $nextExpectedMatch->subDays(); Log::debug(sprintf('Subtract %d days from next expected match, which is now %s', $diffEOM, $nextExpectedMatch->format('Y-m-d'))); } } diff --git a/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php b/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php index 0c77493667..b4a774ea95 100644 --- a/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php +++ b/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php @@ -227,7 +227,7 @@ trait RecalculatesAvailableBudgetsTrait } // prep for next loop - $current = app('navigation')->addPeriod($current, $viewRange, 0); + $current = app('navigation')->addPeriod($current, $viewRange); } } } diff --git a/app/Support/ParseDateString.php b/app/Support/ParseDateString.php index df60be3d1d..45404e1952 100644 --- a/app/Support/ParseDateString.php +++ b/app/Support/ParseDateString.php @@ -30,6 +30,7 @@ use Carbon\Exceptions\InvalidFormatException; use FireflyIII\Exceptions\FireflyException; use Illuminate\Support\Facades\Log; +use Safe\Exceptions\PcreException; use function Safe\preg_match; /** @@ -72,8 +73,11 @@ class ParseDateString } /** - * @throws FireflyException + * @param string $date * + * @return Carbon + * @throws FireflyException + * @throws PcreException * @SuppressWarnings("PHPMD.NPathComplexity") */ public function parseDate(string $date): Carbon diff --git a/app/Support/Preferences.php b/app/Support/Preferences.php index d622f1f66f..5f00a32036 100644 --- a/app/Support/Preferences.php +++ b/app/Support/Preferences.php @@ -231,7 +231,6 @@ class Preferences } /** - * @throws FireflyException */ public function lastActivity(): string { diff --git a/app/Support/Report/Budget/BudgetReportGenerator.php b/app/Support/Report/Budget/BudgetReportGenerator.php index 4de1c9d9a6..1bf9c81b3e 100644 --- a/app/Support/Report/Budget/BudgetReportGenerator.php +++ b/app/Support/Report/Budget/BudgetReportGenerator.php @@ -133,7 +133,6 @@ class BudgetReportGenerator } /** - * @throws FireflyException */ public function setUser(User $user): void { diff --git a/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php b/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php index 2fc216493d..9587ccc0f7 100644 --- a/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php +++ b/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php @@ -80,7 +80,7 @@ trait CalculateXOccurrencesSince while ($total < $count) { $domCorrected = min($dayOfMonth, $mutator->daysInMonth); $mutator->day = $domCorrected; - $mutator->setTime(0, 0, 0); + $mutator->setTime(0, 0); if (0 === $attempts % $skipMod && $mutator->gte($afterDate)) { Log::debug(sprintf('Mutator is now %s and is added to the list.', $mutator->toAtomString())); $return[] = clone $mutator; diff --git a/app/Support/Search/OperatorQuerySearch.php b/app/Support/Search/OperatorQuerySearch.php index 5b2cc0776c..c9368d74af 100644 --- a/app/Support/Search/OperatorQuerySearch.php +++ b/app/Support/Search/OperatorQuerySearch.php @@ -370,7 +370,6 @@ class OperatorQuerySearch implements SearchInterface } /** - * @throws FireflyException */ private function parseDateRange(string $type, string $value): array { diff --git a/app/Support/Search/QueryParser/GdbotsQueryParser.php b/app/Support/Search/QueryParser/GdbotsQueryParser.php index e532901696..127dc475b5 100644 --- a/app/Support/Search/QueryParser/GdbotsQueryParser.php +++ b/app/Support/Search/QueryParser/GdbotsQueryParser.php @@ -31,6 +31,7 @@ use Gdbots\QueryParser\Node as GdbotsNode; use Gdbots\QueryParser\QueryParser as BaseQueryParser; use Illuminate\Support\Facades\Log; use LogicException; +use Safe\Exceptions\FilesystemException; use TypeError; use function Safe\fwrite; @@ -45,7 +46,11 @@ class GdbotsQueryParser implements QueryParserInterface } /** + * @param string $query + * + * @return NodeGroup * @throws FireflyException + * @throws FilesystemException */ public function parse(string $query): NodeGroup { diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 71d5e788f3..9488fe36fb 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -37,6 +37,8 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; use ValueError; use function Safe\parse_url; @@ -534,7 +536,6 @@ class Steam } /** - * @throws FireflyException */ public function getHostName(string $ipAddress): string { @@ -557,7 +558,10 @@ class Steam /** * Get user's language. * + * @return string * @throws FireflyException + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function getLanguage(): string // get preference { diff --git a/app/Support/System/OAuthKeys.php b/app/Support/System/OAuthKeys.php index 97bd74bd19..e4878d01cf 100644 --- a/app/Support/System/OAuthKeys.php +++ b/app/Support/System/OAuthKeys.php @@ -32,6 +32,7 @@ use Laravel\Passport\Console\KeysCommand; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; +use Safe\Exceptions\FilesystemException; use function Safe\file_get_contents; use function Safe\file_put_contents; @@ -79,7 +80,11 @@ class OAuthKeys } /** + * @return bool + * @throws ContainerExceptionInterface * @throws FireflyException + * @throws NotFoundExceptionInterface + * @throws FilesystemException */ public static function restoreKeysFromDB(): bool { diff --git a/app/Transformers/PiggyBankEventTransformer.php b/app/Transformers/PiggyBankEventTransformer.php index a0a43ecc59..d5097b6136 100644 --- a/app/Transformers/PiggyBankEventTransformer.php +++ b/app/Transformers/PiggyBankEventTransformer.php @@ -36,7 +36,7 @@ use FireflyIII\Support\Facades\Steam; class PiggyBankEventTransformer extends AbstractTransformer { private readonly TransactionCurrency $primaryCurrency; - private bool $convertToPrimary = false; + private bool $convertToPrimary; /** * PiggyBankEventTransformer constructor. diff --git a/app/Transformers/PiggyBankTransformer.php b/app/Transformers/PiggyBankTransformer.php index 10c3379441..ccc7e70b00 100644 --- a/app/Transformers/PiggyBankTransformer.php +++ b/app/Transformers/PiggyBankTransformer.php @@ -47,7 +47,6 @@ class PiggyBankTransformer extends AbstractTransformer /** * Transform the piggy bank. * - * @throws FireflyException */ public function transform(PiggyBank $piggyBank): array { diff --git a/app/Transformers/RecurrenceTransformer.php b/app/Transformers/RecurrenceTransformer.php index 9f3ce0c5f0..00c388fdcb 100644 --- a/app/Transformers/RecurrenceTransformer.php +++ b/app/Transformers/RecurrenceTransformer.php @@ -41,7 +41,6 @@ class RecurrenceTransformer extends AbstractTransformer /** * Transform the recurring transaction. * - * @throws FireflyException */ public function transform(Recurrence $recurrence): array { diff --git a/app/Transformers/UserTransformer.php b/app/Transformers/UserTransformer.php index 18dc74d4b6..cff73173c2 100644 --- a/app/Transformers/UserTransformer.php +++ b/app/Transformers/UserTransformer.php @@ -38,7 +38,6 @@ class UserTransformer extends AbstractTransformer /** * Transform user. * - * @throws FireflyException */ public function transform(User $user): array { diff --git a/app/User.php b/app/User.php index 3e2eccddf0..cc9e4b7d07 100644 --- a/app/User.php +++ b/app/User.php @@ -259,7 +259,6 @@ class User extends Authenticatable $dbRolesIds = $dbRoles->pluck('id')->toArray(); $dbRolesTitles = $dbRoles->pluck('title')->toArray(); - /** @var Collection $groupMemberships */ $groupMemberships = $this->groupMemberships()->whereIn('user_role_id', $dbRolesIds)->where('user_group_id', $userGroup->id)->get(); if (0 === $groupMemberships->count()) { app('log')->error(sprintf( diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index bb9bbd3198..e3d25b33f8 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -46,6 +46,9 @@ use PragmaRX\Google2FA\Exceptions\InvalidCharactersException; use PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException; use PragmaRX\Google2FALaravel\Facade; use Config; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; +use Safe\Exceptions\JsonException; use ValueError; use function Safe\preg_match; @@ -62,10 +65,12 @@ class FireflyValidator extends Validator * @param mixed $attribute * @param mixed $value * + * @return bool * @throws IncompatibleWithGoogleAuthenticatorException * @throws InvalidCharactersException * @throws SecretKeyTooShortException - * + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ public function validate2faCode($attribute, $value): bool @@ -628,6 +633,8 @@ class FireflyValidator extends Validator * @param mixed $value * @param mixed $parameters * + * @return bool + * @throws JsonException * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ public function validateUniqueAccountNumberForUser($attribute, $value, $parameters): bool From 50279d623c997fc30c3ea504c0508b55cc045fc0 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 5 Oct 2025 12:59:43 +0200 Subject: [PATCH 81/91] More code cleanup --- .../Controllers/Autocomplete/TransactionController.php | 2 +- app/Api/V1/Controllers/Data/Export/ExportController.php | 1 - app/Api/V1/Controllers/Models/Account/ListController.php | 1 - app/Api/V1/Controllers/Models/Account/ShowController.php | 1 - .../Models/AvailableBudget/ShowController.php | 1 - app/Api/V1/Controllers/Models/Bill/ListController.php | 1 - app/Api/V1/Controllers/Models/Bill/ShowController.php | 1 - app/Api/V1/Controllers/Models/Budget/ListController.php | 1 - app/Api/V1/Controllers/Models/Budget/ShowController.php | 1 - .../V1/Controllers/Models/BudgetLimit/ListController.php | 1 - .../V1/Controllers/Models/Category/ListController.php | 1 - .../V1/Controllers/Models/Category/ShowController.php | 1 - .../V1/Controllers/Models/ObjectGroup/ListController.php | 1 - .../V1/Controllers/Models/PiggyBank/ListController.php | 1 - .../V1/Controllers/Models/PiggyBank/ShowController.php | 1 - .../V1/Controllers/Models/Recurrence/ListController.php | 1 - .../V1/Controllers/Models/Recurrence/ShowController.php | 1 - app/Api/V1/Controllers/Models/Rule/ShowController.php | 1 - .../V1/Controllers/Models/RuleGroup/ListController.php | 1 - .../V1/Controllers/Models/RuleGroup/ShowController.php | 1 - app/Api/V1/Controllers/Models/Tag/ListController.php | 1 - app/Api/V1/Controllers/Models/Tag/ShowController.php | 1 - .../V1/Controllers/Models/Transaction/ListController.php | 1 - .../V1/Controllers/Models/Transaction/ShowController.php | 1 - .../Models/TransactionCurrency/DestroyController.php | 2 +- .../Models/TransactionCurrency/ListController.php | 1 - .../Models/TransactionCurrency/ShowController.php | 1 - .../Models/TransactionCurrency/StoreController.php | 1 - .../Models/TransactionLink/ShowController.php | 1 - .../Models/TransactionLink/StoreController.php | 2 +- .../Models/TransactionLinkType/ListController.php | 1 - .../Models/TransactionLinkType/ShowController.php | 1 - .../Models/TransactionLinkType/StoreController.php | 2 +- .../Models/TransactionLinkType/UpdateController.php | 2 +- app/Api/V1/Controllers/Search/TransactionController.php | 1 - app/Api/V1/Controllers/Summary/BasicController.php | 2 +- app/Api/V1/Controllers/Webhook/ShowController.php | 1 - app/Api/V1/Requests/Chart/ChartRequest.php | 2 +- .../V1/Requests/Data/Bulk/MoveTransactionsRequest.php | 2 +- app/Api/V1/Requests/Data/Bulk/TransactionRequest.php | 5 ++--- app/Api/V1/Requests/Models/Account/ShowRequest.php | 2 +- app/Api/V1/Requests/Models/Account/UpdateRequest.php | 2 +- app/Api/V1/Requests/Models/AvailableBudget/Request.php | 2 +- app/Api/V1/Requests/Models/Bill/StoreRequest.php | 6 +++--- app/Api/V1/Requests/Models/Bill/UpdateRequest.php | 2 +- app/Api/V1/Requests/Models/Budget/StoreRequest.php | 2 +- app/Api/V1/Requests/Models/Budget/UpdateRequest.php | 2 +- app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php | 4 ++-- .../CurrencyExchangeRate/StoreByCurrenciesRequest.php | 2 +- .../Models/CurrencyExchangeRate/StoreByDateRequest.php | 2 +- app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php | 4 ++-- app/Api/V1/Requests/Models/Recurrence/StoreRequest.php | 2 +- app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php | 2 +- app/Api/V1/Requests/Models/Rule/StoreRequest.php | 2 +- app/Api/V1/Requests/Models/Rule/UpdateRequest.php | 2 +- app/Api/V1/Requests/Models/Transaction/StoreRequest.php | 2 +- app/Api/V1/Requests/Models/Transaction/UpdateRequest.php | 2 +- .../V1/Requests/Models/TransactionLink/StoreRequest.php | 2 +- .../V1/Requests/Models/TransactionLink/UpdateRequest.php | 2 +- app/Api/V1/Requests/System/UserUpdateRequest.php | 2 +- app/Console/Commands/Correction/CorrectsUnevenAmount.php | 2 +- app/Console/Commands/Correction/CreatesAccessTokens.php | 2 +- app/Console/Commands/Correction/RemovesEmptyGroups.php | 2 +- .../Commands/Correction/RemovesOrphanedTransactions.php | 2 +- app/Console/Commands/Export/ExportsData.php | 5 ++--- app/Console/Commands/System/ForcesDecimalSize.php | 4 +--- app/Console/Commands/System/ResetsErrorMailLimit.php | 1 - app/Console/Commands/System/ScansAttachments.php | 1 - app/Console/Commands/System/VerifySecurityAlerts.php | 1 - .../Commands/Upgrade/AddsTransactionIdentifiers.php | 1 - .../Commands/Upgrade/RemovesDatabaseDecryption.php | 1 - .../Commands/Upgrade/UpgradesAccountCurrencies.php | 3 +-- app/Console/Commands/Upgrade/UpgradesDatabase.php | 6 ++---- .../Commands/Upgrade/UpgradesRecurrenceMetaData.php | 1 - app/Console/Commands/Upgrade/UpgradesToGroups.php | 2 +- app/Exceptions/GracefulNotFoundHandler.php | 2 +- app/Exceptions/Handler.php | 7 +++---- app/Exceptions/IntervalException.php | 2 +- app/Factory/BillFactory.php | 2 +- app/Factory/PiggyBankFactory.php | 5 ++--- app/Factory/TransactionJournalFactory.php | 7 +++---- app/Handlers/Events/APIEventHandler.php | 2 +- app/Handlers/Events/AdminEventHandler.php | 2 +- app/Handlers/Events/AutomationHandler.php | 2 +- app/Handlers/Events/BillEventHandler.php | 1 - app/Handlers/Events/Model/PiggyBankEventHandler.php | 4 ++-- app/Handlers/Events/Security/MFAHandler.php | 2 +- app/Handlers/Events/UserEventHandler.php | 2 +- app/Handlers/Events/VersionCheckEventHandler.php | 2 +- app/Helpers/Attachments/AttachmentHelper.php | 9 +++------ app/Helpers/Collector/Extensions/MetaCollection.php | 1 - app/Helpers/Collector/GroupCollector.php | 1 - app/Helpers/Report/NetWorth.php | 2 +- app/Helpers/Report/NetWorthInterface.php | 2 +- app/Helpers/Webhook/Sha3SignatureGenerator.php | 1 - app/Http/Controllers/Account/CreateController.php | 1 - app/Http/Controllers/Account/EditController.php | 2 +- app/Http/Controllers/Account/IndexController.php | 1 - app/Http/Controllers/AttachmentController.php | 1 - app/Http/Controllers/Auth/ForgotPasswordController.php | 1 - app/Http/Controllers/Auth/LoginController.php | 4 ++-- app/Http/Controllers/Bill/IndexController.php | 1 - app/Http/Controllers/Budget/EditController.php | 2 +- app/Http/Controllers/Budget/ShowController.php | 2 +- app/Http/Controllers/Category/NoCategoryController.php | 2 +- app/Http/Controllers/Chart/AccountController.php | 1 - app/Http/Controllers/Chart/CategoryController.php | 1 - app/Http/Controllers/Chart/PiggyBankController.php | 1 - app/Http/Controllers/Controller.php | 5 ++--- app/Http/Controllers/DebugController.php | 2 -- app/Http/Controllers/Export/IndexController.php | 2 +- app/Http/Controllers/HomeController.php | 2 +- app/Http/Controllers/Json/BoxController.php | 2 +- app/Http/Controllers/Json/BudgetController.php | 2 +- app/Http/Controllers/Json/FrontpageController.php | 2 +- app/Http/Controllers/Json/IntroController.php | 1 - app/Http/Controllers/Json/ReconcileController.php | 2 +- app/Http/Controllers/Json/RuleController.php | 2 +- app/Http/Controllers/NewUserController.php | 3 +-- app/Http/Controllers/PiggyBank/IndexController.php | 1 - app/Http/Controllers/PreferencesController.php | 7 +++---- app/Http/Controllers/Profile/MfaController.php | 5 ++--- app/Http/Controllers/ProfileController.php | 4 ++-- app/Http/Controllers/Report/AccountController.php | 2 +- app/Http/Controllers/Report/BalanceController.php | 2 +- app/Http/Controllers/Report/BillController.php | 2 +- app/Http/Controllers/Report/BudgetController.php | 4 ++-- app/Http/Controllers/Report/CategoryController.php | 2 +- app/Http/Controllers/Report/DoubleController.php | 2 +- app/Http/Controllers/Report/OperationsController.php | 2 +- app/Http/Controllers/Report/TagController.php | 2 +- app/Http/Controllers/Rule/CreateController.php | 2 +- app/Http/Controllers/Rule/EditController.php | 2 +- app/Http/Controllers/Rule/SelectController.php | 2 +- app/Http/Controllers/SearchController.php | 2 +- app/Http/Controllers/System/InstallController.php | 7 +++---- app/Http/Controllers/TagController.php | 2 +- app/Http/Controllers/Transaction/ConvertController.php | 4 ++-- app/Http/Controllers/Transaction/CreateController.php | 4 +--- app/Http/Controllers/Transaction/EditController.php | 1 - app/Http/Controllers/Transaction/IndexController.php | 2 +- app/Http/Controllers/Transaction/MassController.php | 2 +- .../Controllers/TransactionCurrency/CreateController.php | 2 +- app/Http/Middleware/AcceptHeaders.php | 1 - app/Http/Middleware/Range.php | 6 +++--- app/Http/Middleware/SecureHeaders.php | 2 +- app/Http/Middleware/StartFireflySession.php | 2 +- app/Http/Middleware/TrustHosts.php | 2 +- app/Http/Requests/AccountFormRequest.php | 2 +- app/Http/Requests/AttachmentFormRequest.php | 2 +- app/Http/Requests/BillStoreRequest.php | 2 +- app/Http/Requests/BillUpdateRequest.php | 2 +- app/Http/Requests/BudgetFormStoreRequest.php | 2 +- app/Http/Requests/BudgetFormUpdateRequest.php | 2 +- app/Http/Requests/BudgetIncomeRequest.php | 2 +- app/Http/Requests/BulkEditJournalRequest.php | 2 +- app/Http/Requests/CategoryFormRequest.php | 2 +- app/Http/Requests/ConfigurationRequest.php | 2 +- app/Http/Requests/CurrencyFormRequest.php | 2 +- app/Http/Requests/DeleteAccountFormRequest.php | 2 +- app/Http/Requests/EmailFormRequest.php | 2 +- app/Http/Requests/ExistingTokenFormRequest.php | 2 +- app/Http/Requests/InviteUserFormRequest.php | 2 +- app/Http/Requests/JournalLinkRequest.php | 2 +- app/Http/Requests/LinkTypeFormRequest.php | 2 +- app/Http/Requests/MassDeleteJournalRequest.php | 2 +- app/Http/Requests/MassEditJournalRequest.php | 2 +- app/Http/Requests/NewUserFormRequest.php | 2 +- app/Http/Requests/ObjectGroupFormRequest.php | 2 +- app/Http/Requests/PiggyBankStoreRequest.php | 2 +- app/Http/Requests/PiggyBankUpdateRequest.php | 2 +- app/Http/Requests/ProfileFormRequest.php | 2 +- app/Http/Requests/ReconciliationStoreRequest.php | 2 +- app/Http/Requests/RecurrenceFormRequest.php | 2 +- app/Http/Requests/ReportFormRequest.php | 3 +-- app/Http/Requests/RuleFormRequest.php | 2 +- app/Http/Requests/RuleGroupFormRequest.php | 2 +- app/Http/Requests/SelectTransactionsRequest.php | 2 +- app/Http/Requests/TagFormRequest.php | 2 +- app/Http/Requests/TestRuleFormRequest.php | 2 +- app/Http/Requests/TokenFormRequest.php | 2 +- app/Http/Requests/TriggerRecurrenceRequest.php | 2 +- app/Http/Requests/UserFormRequest.php | 2 +- app/Http/Requests/UserRegistrationRequest.php | 2 +- app/Jobs/DownloadExchangeRates.php | 3 +-- app/Jobs/MailError.php | 7 +++---- app/Mail/InvitationMail.php | 1 - app/Models/AccountMeta.php | 1 - app/Models/Configuration.php | 1 - app/Models/TransactionJournalMeta.php | 1 - app/Providers/AppServiceProvider.php | 1 - app/Providers/FireflyServiceProvider.php | 2 +- app/Repositories/Account/AccountRepository.php | 1 - app/Repositories/Attachment/AttachmentRepository.php | 2 +- app/Repositories/Bill/BillRepository.php | 2 +- app/Repositories/Budget/AvailableBudgetRepository.php | 2 +- .../Budget/AvailableBudgetRepositoryInterface.php | 2 +- app/Repositories/Budget/NoBudgetRepositoryInterface.php | 2 +- app/Repositories/Category/CategoryRepository.php | 2 +- app/Repositories/Currency/CurrencyRepository.php | 1 - app/Repositories/PiggyBank/PiggyBankRepository.php | 4 ++-- app/Repositories/Recurring/RecurringRepository.php | 3 +-- app/Repositories/Rule/RuleRepository.php | 2 +- app/Repositories/RuleGroup/RuleGroupRepository.php | 2 +- app/Repositories/Tag/TagRepository.php | 4 ++-- .../TransactionGroup/TransactionGroupRepository.php | 1 - app/Repositories/User/UserRepository.php | 2 +- app/Rules/Account/IsValidAccountType.php | 2 +- app/Rules/Admin/IsValidDiscordUrl.php | 2 +- app/Rules/Admin/IsValidSlackOrDiscordUrl.php | 2 +- app/Rules/Admin/IsValidSlackUrl.php | 2 +- app/Rules/BelongsUser.php | 2 +- app/Rules/BelongsUserGroup.php | 2 +- app/Rules/IsAllowedGroupAction.php | 2 +- app/Rules/IsAssetAccountId.php | 2 +- app/Rules/IsBoolean.php | 2 +- app/Rules/IsDateOrTime.php | 2 +- app/Rules/IsDefaultUserGroupName.php | 2 +- app/Rules/IsDuplicateTransaction.php | 2 +- app/Rules/IsFilterValueIn.php | 2 +- app/Rules/IsTransferAccount.php | 2 +- app/Rules/IsValidActionExpression.php | 2 +- app/Rules/IsValidAmount.php | 2 +- app/Rules/IsValidAttachmentModel.php | 2 +- app/Rules/IsValidBulkClause.php | 3 +-- app/Rules/IsValidDateRange.php | 2 +- app/Rules/IsValidPositiveAmount.php | 3 +-- app/Rules/IsValidZeroOrMoreAmount.php | 2 +- app/Rules/LessThanPiggyTarget.php | 2 +- app/Rules/UniqueAccountNumber.php | 3 +-- app/Rules/UniqueIban.php | 2 +- app/Rules/ValidJournals.php | 2 +- app/Rules/ValidRecurrenceRepetitionType.php | 2 +- app/Rules/ValidRecurrenceRepetitionValue.php | 2 +- app/Services/FireflyIIIOrg/Update/UpdateRequest.php | 1 - app/Services/Internal/Support/AccountServiceTrait.php | 2 +- app/Services/Internal/Support/JournalServiceTrait.php | 1 - .../Internal/Support/RecurringTransactionTrait.php | 1 - app/Services/Internal/Update/BillUpdateService.php | 3 +-- app/Services/Internal/Update/CategoryUpdateService.php | 2 +- app/Services/Webhook/StandardWebhookSender.php | 1 - app/Support/Binder/CLIToken.php | 1 - app/Support/CacheProperties.php | 1 - app/Support/Http/Controllers/CreateStuff.php | 1 - app/Support/Http/Controllers/RequestInformation.php | 1 - app/Support/JsonApi/Enrichments/RecurringEnrichment.php | 1 - app/Support/ParseDateString.php | 1 - app/Support/Report/Budget/BudgetReportGenerator.php | 1 - app/Support/Request/ConvertsDataTypes.php | 1 - app/Support/Search/AccountSearch.php | 1 - app/Support/Search/QueryParser/GdbotsQueryParser.php | 1 - app/Support/Steam.php | 1 - app/Support/System/OAuthKeys.php | 1 - app/Support/Twig/General.php | 1 - app/Support/Twig/TransactionGroupTwig.php | 1 - app/Transformers/PiggyBankTransformer.php | 1 - app/Transformers/RecurrenceTransformer.php | 1 - app/Transformers/UserTransformer.php | 1 - app/Transformers/WebhookMessageTransformer.php | 1 - app/User.php | 2 +- .../Api/Data/Bulk/ValidatesBulkTransactionQuery.php | 3 +-- app/Validation/CurrencyValidation.php | 2 +- app/Validation/FireflyValidator.php | 5 ++--- app/Validation/GroupValidation.php | 2 +- app/Validation/RecurrenceValidation.php | 2 +- app/Validation/TransactionValidation.php | 2 +- 266 files changed, 214 insertions(+), 329 deletions(-) diff --git a/app/Api/V1/Controllers/Autocomplete/TransactionController.php b/app/Api/V1/Controllers/Autocomplete/TransactionController.php index 4321479d26..d088427937 100644 --- a/app/Api/V1/Controllers/Autocomplete/TransactionController.php +++ b/app/Api/V1/Controllers/Autocomplete/TransactionController.php @@ -24,10 +24,10 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Autocomplete; -use FireflyIII\Models\TransactionGroup; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest; use FireflyIII\Enums\UserRoleEnum; +use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface; diff --git a/app/Api/V1/Controllers/Data/Export/ExportController.php b/app/Api/V1/Controllers/Data/Export/ExportController.php index f578b7b050..685217f769 100644 --- a/app/Api/V1/Controllers/Data/Export/ExportController.php +++ b/app/Api/V1/Controllers/Data/Export/ExportController.php @@ -31,7 +31,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Support\Export\ExportDataGenerator; use Illuminate\Http\Response as LaravelResponse; use Safe\Exceptions\DatetimeException; - use function Safe\date; /** diff --git a/app/Api/V1/Controllers/Models/Account/ListController.php b/app/Api/V1/Controllers/Models/Account/ListController.php index 1d646a0388..6ea2908f76 100644 --- a/app/Api/V1/Controllers/Models/Account/ListController.php +++ b/app/Api/V1/Controllers/Models/Account/ListController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\Account; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Account; use FireflyIII\Repositories\Account\AccountRepositoryInterface; diff --git a/app/Api/V1/Controllers/Models/Account/ShowController.php b/app/Api/V1/Controllers/Models/Account/ShowController.php index 5ef0363961..74c5fba684 100644 --- a/app/Api/V1/Controllers/Models/Account/ShowController.php +++ b/app/Api/V1/Controllers/Models/Account/ShowController.php @@ -26,7 +26,6 @@ namespace FireflyIII\Api\V1\Controllers\Models\Account; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Models\Account\ShowRequest; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\Http\Api\AccountFilter; diff --git a/app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php b/app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php index c9693643eb..9ee8b50278 100644 --- a/app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php +++ b/app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\AvailableBudget; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\AvailableBudget; use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface; use FireflyIII\Support\JsonApi\Enrichments\AvailableBudgetEnrichment; diff --git a/app/Api/V1/Controllers/Models/Bill/ListController.php b/app/Api/V1/Controllers/Models/Bill/ListController.php index 24f27edb1e..e6a98f6716 100644 --- a/app/Api/V1/Controllers/Models/Bill/ListController.php +++ b/app/Api/V1/Controllers/Models/Bill/ListController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\Bill; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Bill; use FireflyIII\Repositories\Bill\BillRepositoryInterface; diff --git a/app/Api/V1/Controllers/Models/Bill/ShowController.php b/app/Api/V1/Controllers/Models/Bill/ShowController.php index 380d86ef76..7ca52a322e 100644 --- a/app/Api/V1/Controllers/Models/Bill/ShowController.php +++ b/app/Api/V1/Controllers/Models/Bill/ShowController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\Bill; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Bill; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Support\JsonApi\Enrichments\SubscriptionEnrichment; diff --git a/app/Api/V1/Controllers/Models/Budget/ListController.php b/app/Api/V1/Controllers/Models/Budget/ListController.php index 72a9cae8fb..17b3f95a94 100644 --- a/app/Api/V1/Controllers/Models/Budget/ListController.php +++ b/app/Api/V1/Controllers/Models/Budget/ListController.php @@ -26,7 +26,6 @@ namespace FireflyIII\Api\V1\Controllers\Models\Budget; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Enums\TransactionTypeEnum; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Budget; use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; diff --git a/app/Api/V1/Controllers/Models/Budget/ShowController.php b/app/Api/V1/Controllers/Models/Budget/ShowController.php index 8d9314b85f..55994b1fc0 100644 --- a/app/Api/V1/Controllers/Models/Budget/ShowController.php +++ b/app/Api/V1/Controllers/Models/Budget/ShowController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\Budget; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Budget; use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php b/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php index 0b66da5487..0e8d707900 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\BudgetLimit; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; diff --git a/app/Api/V1/Controllers/Models/Category/ListController.php b/app/Api/V1/Controllers/Models/Category/ListController.php index b89fc38bae..fbadd0c42a 100644 --- a/app/Api/V1/Controllers/Models/Category/ListController.php +++ b/app/Api/V1/Controllers/Models/Category/ListController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\Category; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Category; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; diff --git a/app/Api/V1/Controllers/Models/Category/ShowController.php b/app/Api/V1/Controllers/Models/Category/ShowController.php index 0d19bb5b77..0f827ed175 100644 --- a/app/Api/V1/Controllers/Models/Category/ShowController.php +++ b/app/Api/V1/Controllers/Models/Category/ShowController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\Category; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Category; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Support\JsonApi\Enrichments\CategoryEnrichment; diff --git a/app/Api/V1/Controllers/Models/ObjectGroup/ListController.php b/app/Api/V1/Controllers/Models/ObjectGroup/ListController.php index 81fcc0679a..cac0aa7cc5 100644 --- a/app/Api/V1/Controllers/Models/ObjectGroup/ListController.php +++ b/app/Api/V1/Controllers/Models/ObjectGroup/ListController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\ObjectGroup; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\ObjectGroup; use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepositoryInterface; use FireflyIII\Support\JsonApi\Enrichments\PiggyBankEnrichment; diff --git a/app/Api/V1/Controllers/Models/PiggyBank/ListController.php b/app/Api/V1/Controllers/Models/PiggyBank/ListController.php index 5d4181a425..2eff6ea53c 100644 --- a/app/Api/V1/Controllers/Models/PiggyBank/ListController.php +++ b/app/Api/V1/Controllers/Models/PiggyBank/ListController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\PiggyBank; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\PiggyBank; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment; diff --git a/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php b/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php index 570947db73..5f6274e14e 100644 --- a/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php +++ b/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\PiggyBank; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\PiggyBank; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Support\JsonApi\Enrichments\PiggyBankEnrichment; diff --git a/app/Api/V1/Controllers/Models/Recurrence/ListController.php b/app/Api/V1/Controllers/Models/Recurrence/ListController.php index deb46548a3..ea5ed5786d 100644 --- a/app/Api/V1/Controllers/Models/Recurrence/ListController.php +++ b/app/Api/V1/Controllers/Models/Recurrence/ListController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\Recurrence; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Recurrence; use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; diff --git a/app/Api/V1/Controllers/Models/Recurrence/ShowController.php b/app/Api/V1/Controllers/Models/Recurrence/ShowController.php index 1be11a85f8..d08117d869 100644 --- a/app/Api/V1/Controllers/Models/Recurrence/ShowController.php +++ b/app/Api/V1/Controllers/Models/Recurrence/ShowController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\Recurrence; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Recurrence; use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; use FireflyIII\Support\JsonApi\Enrichments\RecurringEnrichment; diff --git a/app/Api/V1/Controllers/Models/Rule/ShowController.php b/app/Api/V1/Controllers/Models/Rule/ShowController.php index e659f2e8c4..d18599802c 100644 --- a/app/Api/V1/Controllers/Models/Rule/ShowController.php +++ b/app/Api/V1/Controllers/Models/Rule/ShowController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\Rule; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Rule; use FireflyIII\Repositories\Rule\RuleRepositoryInterface; use FireflyIII\Transformers\RuleTransformer; diff --git a/app/Api/V1/Controllers/Models/RuleGroup/ListController.php b/app/Api/V1/Controllers/Models/RuleGroup/ListController.php index 6c7b614c03..d39590f831 100644 --- a/app/Api/V1/Controllers/Models/RuleGroup/ListController.php +++ b/app/Api/V1/Controllers/Models/RuleGroup/ListController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\RuleGroup; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\RuleGroup; use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; use FireflyIII\Transformers\RuleTransformer; diff --git a/app/Api/V1/Controllers/Models/RuleGroup/ShowController.php b/app/Api/V1/Controllers/Models/RuleGroup/ShowController.php index 2cf7833853..cd9d42c2d3 100644 --- a/app/Api/V1/Controllers/Models/RuleGroup/ShowController.php +++ b/app/Api/V1/Controllers/Models/RuleGroup/ShowController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\RuleGroup; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\RuleGroup; use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; use FireflyIII\Transformers\RuleGroupTransformer; diff --git a/app/Api/V1/Controllers/Models/Tag/ListController.php b/app/Api/V1/Controllers/Models/Tag/ListController.php index a29b3a016b..49899a4eec 100644 --- a/app/Api/V1/Controllers/Models/Tag/ListController.php +++ b/app/Api/V1/Controllers/Models/Tag/ListController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\Tag; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Tag; use FireflyIII\Repositories\Tag\TagRepositoryInterface; diff --git a/app/Api/V1/Controllers/Models/Tag/ShowController.php b/app/Api/V1/Controllers/Models/Tag/ShowController.php index b9daf868dc..f69d050ddc 100644 --- a/app/Api/V1/Controllers/Models/Tag/ShowController.php +++ b/app/Api/V1/Controllers/Models/Tag/ShowController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\Tag; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Tag; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Transformers\TagTransformer; diff --git a/app/Api/V1/Controllers/Models/Transaction/ListController.php b/app/Api/V1/Controllers/Models/Transaction/ListController.php index a7e0649017..fbc68db891 100644 --- a/app/Api/V1/Controllers/Models/Transaction/ListController.php +++ b/app/Api/V1/Controllers/Models/Transaction/ListController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\Transaction; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Journal\JournalAPIRepositoryInterface; diff --git a/app/Api/V1/Controllers/Models/Transaction/ShowController.php b/app/Api/V1/Controllers/Models/Transaction/ShowController.php index f16b37b81a..90942fff14 100644 --- a/app/Api/V1/Controllers/Models/Transaction/ShowController.php +++ b/app/Api/V1/Controllers/Models/Transaction/ShowController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\Transaction; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/DestroyController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/DestroyController.php index b7c7e69a34..b4aca8b763 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/DestroyController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/DestroyController.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\TransactionCurrency; -use Illuminate\Support\Facades\Validator; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionCurrency; @@ -32,6 +31,7 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; use Illuminate\Http\JsonResponse; +use Illuminate\Support\Facades\Validator; use Illuminate\Validation\ValidationException; /** diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php index cd8363c8fe..02bec34c6a 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\TransactionCurrency; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Account; use FireflyIII\Models\Bill; diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php index 9fd407ccf4..8f4168ae1e 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\TransactionCurrency; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\Http\Api\AccountFilter; diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php index 01aad04665..bbead26e16 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php @@ -26,7 +26,6 @@ namespace FireflyIII\Api\V1\Controllers\Models\TransactionCurrency; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Models\TransactionCurrency\StoreRequest; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\Http\Api\AccountFilter; use FireflyIII\Support\Http\Api\TransactionFilter; diff --git a/app/Api/V1/Controllers/Models/TransactionLink/ShowController.php b/app/Api/V1/Controllers/Models/TransactionLink/ShowController.php index 1f812f2b18..a45be78c09 100644 --- a/app/Api/V1/Controllers/Models/TransactionLink/ShowController.php +++ b/app/Api/V1/Controllers/Models/TransactionLink/ShowController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\TransactionLink; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionJournalLink; use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; use FireflyIII\Transformers\TransactionLinkTransformer; diff --git a/app/Api/V1/Controllers/Models/TransactionLink/StoreController.php b/app/Api/V1/Controllers/Models/TransactionLink/StoreController.php index 89219ff30a..2d0b04cba8 100644 --- a/app/Api/V1/Controllers/Models/TransactionLink/StoreController.php +++ b/app/Api/V1/Controllers/Models/TransactionLink/StoreController.php @@ -24,10 +24,10 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\TransactionLink; -use FireflyIII\Models\TransactionJournal; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Models\TransactionLink\StoreRequest; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; use FireflyIII\Support\Http\Api\TransactionFilter; diff --git a/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php b/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php index 6ba4e25c4c..a71cfa46e9 100644 --- a/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php +++ b/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\TransactionLinkType; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\LinkType; use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; diff --git a/app/Api/V1/Controllers/Models/TransactionLinkType/ShowController.php b/app/Api/V1/Controllers/Models/TransactionLinkType/ShowController.php index ded5d53f8f..65af2aa929 100644 --- a/app/Api/V1/Controllers/Models/TransactionLinkType/ShowController.php +++ b/app/Api/V1/Controllers/Models/TransactionLinkType/ShowController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\TransactionLinkType; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\LinkType; use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; use FireflyIII\Support\Http\Api\TransactionFilter; diff --git a/app/Api/V1/Controllers/Models/TransactionLinkType/StoreController.php b/app/Api/V1/Controllers/Models/TransactionLinkType/StoreController.php index 7e534e3993..7901c88ec0 100644 --- a/app/Api/V1/Controllers/Models/TransactionLinkType/StoreController.php +++ b/app/Api/V1/Controllers/Models/TransactionLinkType/StoreController.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\TransactionLinkType; -use Illuminate\Support\Facades\Validator; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Models\TransactionLinkType\StoreRequest; use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; @@ -33,6 +32,7 @@ use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Transformers\LinkTypeTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; +use Illuminate\Support\Facades\Validator; use Illuminate\Validation\ValidationException; use League\Fractal\Resource\Item; diff --git a/app/Api/V1/Controllers/Models/TransactionLinkType/UpdateController.php b/app/Api/V1/Controllers/Models/TransactionLinkType/UpdateController.php index 2b43fa81df..cf964f259d 100644 --- a/app/Api/V1/Controllers/Models/TransactionLinkType/UpdateController.php +++ b/app/Api/V1/Controllers/Models/TransactionLinkType/UpdateController.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\TransactionLinkType; -use Illuminate\Support\Facades\Validator; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Models\TransactionLinkType\UpdateRequest; use FireflyIII\Exceptions\FireflyException; @@ -35,6 +34,7 @@ use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Transformers\LinkTypeTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; +use Illuminate\Support\Facades\Validator; use Illuminate\Validation\ValidationException; use League\Fractal\Resource\Item; diff --git a/app/Api/V1/Controllers/Search/TransactionController.php b/app/Api/V1/Controllers/Search/TransactionController.php index 10d754891a..040e672934 100644 --- a/app/Api/V1/Controllers/Search/TransactionController.php +++ b/app/Api/V1/Controllers/Search/TransactionController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Search; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment; use FireflyIII\Support\Search\SearchInterface; use FireflyIII\Transformers\TransactionGroupTransformer; diff --git a/app/Api/V1/Controllers/Summary/BasicController.php b/app/Api/V1/Controllers/Summary/BasicController.php index 7550d63fb7..6b2acc86eb 100644 --- a/app/Api/V1/Controllers/Summary/BasicController.php +++ b/app/Api/V1/Controllers/Summary/BasicController.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Summary; -use Exception; use Carbon\Carbon; +use Exception; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Data\DateRequest; use FireflyIII\Enums\AccountTypeEnum; diff --git a/app/Api/V1/Controllers/Webhook/ShowController.php b/app/Api/V1/Controllers/Webhook/ShowController.php index b4728e2dee..466f0e8a90 100644 --- a/app/Api/V1/Controllers/Webhook/ShowController.php +++ b/app/Api/V1/Controllers/Webhook/ShowController.php @@ -27,7 +27,6 @@ namespace FireflyIII\Api\V1\Controllers\Webhook; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Enums\WebhookTrigger; use FireflyIII\Events\RequestedSendWebhookMessages; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Generator\Webhook\MessageGeneratorInterface; use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\Webhook; diff --git a/app/Api/V1/Requests/Chart/ChartRequest.php b/app/Api/V1/Requests/Chart/ChartRequest.php index d298a89116..bf43fe47c2 100644 --- a/app/Api/V1/Requests/Chart/ChartRequest.php +++ b/app/Api/V1/Requests/Chart/ChartRequest.php @@ -24,13 +24,13 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Chart; -use Illuminate\Validation\Validator; use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class ChartRequest diff --git a/app/Api/V1/Requests/Data/Bulk/MoveTransactionsRequest.php b/app/Api/V1/Requests/Data/Bulk/MoveTransactionsRequest.php index 6e8f9b285c..3ada80f591 100644 --- a/app/Api/V1/Requests/Data/Bulk/MoveTransactionsRequest.php +++ b/app/Api/V1/Requests/Data/Bulk/MoveTransactionsRequest.php @@ -24,12 +24,12 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Data\Bulk; -use Illuminate\Validation\Validator; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class MoveTransactionsRequest diff --git a/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php b/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php index 46aea15783..14b660d7fe 100644 --- a/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php +++ b/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php @@ -24,8 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Data\Bulk; -use Illuminate\Validation\Validator; -use JsonException; use FireflyIII\Enums\ClauseType; use FireflyIII\Rules\IsValidBulkClause; use FireflyIII\Support\Request\ChecksLogin; @@ -33,7 +31,8 @@ use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Validation\Api\Data\Bulk\ValidatesBulkTransactionQuery; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; - +use Illuminate\Validation\Validator; +use JsonException; use function Safe\json_decode; /** diff --git a/app/Api/V1/Requests/Models/Account/ShowRequest.php b/app/Api/V1/Requests/Models/Account/ShowRequest.php index 6d6dcb71c5..f4f7d73332 100644 --- a/app/Api/V1/Requests/Models/Account/ShowRequest.php +++ b/app/Api/V1/Requests/Models/Account/ShowRequest.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Account; -use Illuminate\Validation\Validator; use Carbon\Carbon; use FireflyIII\Models\Account; use FireflyIII\Rules\IsValidSortInstruction; @@ -32,6 +31,7 @@ use FireflyIII\Support\Http\Api\AccountFilter; use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\User; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\Validator; class ShowRequest extends FormRequest { diff --git a/app/Api/V1/Requests/Models/Account/UpdateRequest.php b/app/Api/V1/Requests/Models/Account/UpdateRequest.php index 473e5b8714..dbe30eab1b 100644 --- a/app/Api/V1/Requests/Models/Account/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Account/UpdateRequest.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Account; -use Illuminate\Validation\Validator; use FireflyIII\Models\Account; use FireflyIII\Models\Location; use FireflyIII\Repositories\Account\AccountRepositoryInterface; @@ -36,6 +35,7 @@ use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class UpdateRequest diff --git a/app/Api/V1/Requests/Models/AvailableBudget/Request.php b/app/Api/V1/Requests/Models/AvailableBudget/Request.php index 02cbf088e6..c2ddea145c 100644 --- a/app/Api/V1/Requests/Models/AvailableBudget/Request.php +++ b/app/Api/V1/Requests/Models/AvailableBudget/Request.php @@ -24,13 +24,13 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\AvailableBudget; -use Illuminate\Validation\Validator; use Carbon\Carbon; use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class Request diff --git a/app/Api/V1/Requests/Models/Bill/StoreRequest.php b/app/Api/V1/Requests/Models/Bill/StoreRequest.php index 4b35c5bb2e..393ff54ca3 100644 --- a/app/Api/V1/Requests/Models/Bill/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Bill/StoreRequest.php @@ -24,15 +24,15 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Bill; -use Illuminate\Validation\Validator; -use ValueError; -use TypeError; use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; +use TypeError; +use ValueError; /** * Class StoreRequest diff --git a/app/Api/V1/Requests/Models/Bill/UpdateRequest.php b/app/Api/V1/Requests/Models/Bill/UpdateRequest.php index f4dd92f9df..8b16cce5e3 100644 --- a/app/Api/V1/Requests/Models/Bill/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Bill/UpdateRequest.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Bill; -use Illuminate\Validation\Validator; use FireflyIII\Models\Bill; use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsValidPositiveAmount; @@ -32,6 +31,7 @@ use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class UpdateRequest diff --git a/app/Api/V1/Requests/Models/Budget/StoreRequest.php b/app/Api/V1/Requests/Models/Budget/StoreRequest.php index fd36873cbb..9d3a9bc883 100644 --- a/app/Api/V1/Requests/Models/Budget/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Budget/StoreRequest.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Budget; -use Illuminate\Validation\Validator; use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; @@ -32,6 +31,7 @@ use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Validation\AutoBudget\ValidatesAutoBudgetRequest; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class StoreRequest diff --git a/app/Api/V1/Requests/Models/Budget/UpdateRequest.php b/app/Api/V1/Requests/Models/Budget/UpdateRequest.php index 870cc3294f..d028906aa4 100644 --- a/app/Api/V1/Requests/Models/Budget/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Budget/UpdateRequest.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Budget; -use Illuminate\Validation\Validator; use FireflyIII\Models\Budget; use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsValidPositiveAmount; @@ -33,6 +32,7 @@ use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Validation\AutoBudget\ValidatesAutoBudgetRequest; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class UpdateRequest diff --git a/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php b/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php index 5262ab4427..f4ecfa9ac4 100644 --- a/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php @@ -24,14 +24,14 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\BudgetLimit; -use FireflyIII\Rules\IsBoolean; -use Illuminate\Validation\Validator; use Carbon\Carbon; +use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class UpdateRequest diff --git a/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByCurrenciesRequest.php b/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByCurrenciesRequest.php index be2bfe8584..b7a8d00159 100644 --- a/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByCurrenciesRequest.php +++ b/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByCurrenciesRequest.php @@ -28,8 +28,8 @@ use Carbon\Carbon; use Carbon\Exceptions\InvalidFormatException; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; -use Illuminate\Validation\Validator; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\Validator; class StoreByCurrenciesRequest extends FormRequest { diff --git a/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByDateRequest.php b/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByDateRequest.php index aa1eb2a5b7..de5dbe94c9 100644 --- a/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByDateRequest.php +++ b/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByDateRequest.php @@ -24,13 +24,13 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate; -use Illuminate\Validation\Validator; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\Validator; class StoreByDateRequest extends FormRequest { diff --git a/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php b/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php index 0810b4ce4b..81acd25bd7 100644 --- a/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php +++ b/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php @@ -24,15 +24,15 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\PiggyBank; -use Illuminate\Validation\Validator; -use FireflyIII\Support\Facades\Amount; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Rules\IsValidZeroOrMoreAmount; +use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class StoreRequest diff --git a/app/Api/V1/Requests/Models/Recurrence/StoreRequest.php b/app/Api/V1/Requests/Models/Recurrence/StoreRequest.php index 98b15ae55b..387f28f930 100644 --- a/app/Api/V1/Requests/Models/Recurrence/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Recurrence/StoreRequest.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Recurrence; -use Illuminate\Validation\Validator; use FireflyIII\Rules\BelongsUser; use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsValidPositiveAmount; @@ -36,6 +35,7 @@ use FireflyIII\Validation\RecurrenceValidation; use FireflyIII\Validation\TransactionValidation; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class StoreRequest diff --git a/app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php b/app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php index 1b5c1590a4..ea5cbd0f9a 100644 --- a/app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Recurrence; -use Illuminate\Validation\Validator; use FireflyIII\Models\Recurrence; use FireflyIII\Rules\BelongsUser; use FireflyIII\Rules\IsBoolean; @@ -37,6 +36,7 @@ use FireflyIII\Validation\RecurrenceValidation; use FireflyIII\Validation\TransactionValidation; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class UpdateRequest diff --git a/app/Api/V1/Requests/Models/Rule/StoreRequest.php b/app/Api/V1/Requests/Models/Rule/StoreRequest.php index 5f9e0126c8..dd65d648a0 100644 --- a/app/Api/V1/Requests/Models/Rule/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Rule/StoreRequest.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Rule; -use Illuminate\Validation\Validator; use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsValidActionExpression; use FireflyIII\Support\Request\ChecksLogin; @@ -32,6 +31,7 @@ use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\GetRuleConfiguration; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class StoreRequest diff --git a/app/Api/V1/Requests/Models/Rule/UpdateRequest.php b/app/Api/V1/Requests/Models/Rule/UpdateRequest.php index 22880b581a..13a8a97676 100644 --- a/app/Api/V1/Requests/Models/Rule/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Rule/UpdateRequest.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Rule; -use Illuminate\Validation\Validator; use FireflyIII\Models\Rule; use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsValidActionExpression; @@ -33,6 +32,7 @@ use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\GetRuleConfiguration; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class UpdateRequest diff --git a/app/Api/V1/Requests/Models/Transaction/StoreRequest.php b/app/Api/V1/Requests/Models/Transaction/StoreRequest.php index 4ad8a851cb..2b54cb977c 100644 --- a/app/Api/V1/Requests/Models/Transaction/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Transaction/StoreRequest.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Transaction; -use Illuminate\Validation\Validator; use FireflyIII\Models\Location; use FireflyIII\Rules\BelongsUser; use FireflyIII\Rules\IsBoolean; @@ -40,6 +39,7 @@ use FireflyIII\Validation\GroupValidation; use FireflyIII\Validation\TransactionValidation; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class StoreRequest diff --git a/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php b/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php index 023a48169e..af1b57cb29 100644 --- a/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Transaction; -use Illuminate\Validation\Validator; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionGroup; use FireflyIII\Rules\BelongsUser; @@ -38,6 +37,7 @@ use FireflyIII\Validation\GroupValidation; use FireflyIII\Validation\TransactionValidation; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class UpdateRequest diff --git a/app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php b/app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php index 693c109d5d..638bda8306 100644 --- a/app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php +++ b/app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\TransactionLink; -use Illuminate\Validation\Validator; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; use FireflyIII\Support\Request\ChecksLogin; @@ -32,6 +31,7 @@ use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\User; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class StoreRequest diff --git a/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php b/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php index 71a8090810..0ab51bccd0 100644 --- a/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\TransactionLink; -use Illuminate\Validation\Validator; use FireflyIII\Models\TransactionJournalLink; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; @@ -32,6 +31,7 @@ use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class UpdateRequest diff --git a/app/Api/V1/Requests/System/UserUpdateRequest.php b/app/Api/V1/Requests/System/UserUpdateRequest.php index 03b201b6ad..553f8e54e4 100644 --- a/app/Api/V1/Requests/System/UserUpdateRequest.php +++ b/app/Api/V1/Requests/System/UserUpdateRequest.php @@ -24,13 +24,13 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\System; -use Illuminate\Validation\Validator; use FireflyIII\Rules\IsBoolean; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\User; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class UserUpdateRequest diff --git a/app/Console/Commands/Correction/CorrectsUnevenAmount.php b/app/Console/Commands/Correction/CorrectsUnevenAmount.php index cc34405706..4f6519951c 100644 --- a/app/Console/Commands/Correction/CorrectsUnevenAmount.php +++ b/app/Console/Commands/Correction/CorrectsUnevenAmount.php @@ -35,8 +35,8 @@ use FireflyIII\Support\Models\AccountBalanceCalculator; use Illuminate\Console\Command; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; -use ValueError; use stdClass; +use ValueError; class CorrectsUnevenAmount extends Command { diff --git a/app/Console/Commands/Correction/CreatesAccessTokens.php b/app/Console/Commands/Correction/CreatesAccessTokens.php index f73e8d2258..92603c83a6 100644 --- a/app/Console/Commands/Correction/CreatesAccessTokens.php +++ b/app/Console/Commands/Correction/CreatesAccessTokens.php @@ -24,11 +24,11 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Correction; +use Exception; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; use Illuminate\Console\Command; -use Exception; class CreatesAccessTokens extends Command { diff --git a/app/Console/Commands/Correction/RemovesEmptyGroups.php b/app/Console/Commands/Correction/RemovesEmptyGroups.php index 2fac48e95b..9058b2f2ab 100644 --- a/app/Console/Commands/Correction/RemovesEmptyGroups.php +++ b/app/Console/Commands/Correction/RemovesEmptyGroups.php @@ -24,10 +24,10 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Correction; +use Exception; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Models\TransactionGroup; use Illuminate\Console\Command; -use Exception; class RemovesEmptyGroups extends Command { diff --git a/app/Console/Commands/Correction/RemovesOrphanedTransactions.php b/app/Console/Commands/Correction/RemovesOrphanedTransactions.php index 86eadad492..2fd8e94dd2 100644 --- a/app/Console/Commands/Correction/RemovesOrphanedTransactions.php +++ b/app/Console/Commands/Correction/RemovesOrphanedTransactions.php @@ -24,11 +24,11 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Correction; +use Exception; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use Illuminate\Console\Command; -use Exception; use stdClass; /** diff --git a/app/Console/Commands/Export/ExportsData.php b/app/Console/Commands/Export/ExportsData.php index 18b1df5a34..cd1d53e9f5 100644 --- a/app/Console/Commands/Export/ExportsData.php +++ b/app/Console/Commands/Export/ExportsData.php @@ -24,22 +24,21 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Export; -use FireflyIII\Models\TransactionJournal; use Carbon\Carbon; +use Exception; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Console\Commands\VerifiesAccessToken; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; +use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Support\Export\ExportDataGenerator; use Illuminate\Console\Command; use Illuminate\Support\Collection; -use Exception; use Illuminate\Support\Facades\Log; use InvalidArgumentException; - use Safe\Exceptions\FilesystemException; use function Safe\file_put_contents; diff --git a/app/Console/Commands/System/ForcesDecimalSize.php b/app/Console/Commands/System/ForcesDecimalSize.php index 4f1ec2164e..c92183b8c3 100644 --- a/app/Console/Commands/System/ForcesDecimalSize.php +++ b/app/Console/Commands/System/ForcesDecimalSize.php @@ -40,12 +40,10 @@ use FireflyIII\Models\TransactionCurrency; use Illuminate\Console\Command; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; -use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; - use Illuminate\Support\Facades\Log; -use function Safe\mb_regex_encoding; use function Safe\json_encode; +use function Safe\mb_regex_encoding; /** * This command was inspired by https://github.com/elliot-gh. It will check all amount fields diff --git a/app/Console/Commands/System/ResetsErrorMailLimit.php b/app/Console/Commands/System/ResetsErrorMailLimit.php index ffba1aad82..c896842a17 100644 --- a/app/Console/Commands/System/ResetsErrorMailLimit.php +++ b/app/Console/Commands/System/ResetsErrorMailLimit.php @@ -28,7 +28,6 @@ namespace FireflyIII\Console\Commands\System; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use Illuminate\Console\Command; use Symfony\Component\Console\Command\Command as CommandAlias; - use function Safe\file_put_contents; use function Safe\json_encode; diff --git a/app/Console/Commands/System/ScansAttachments.php b/app/Console/Commands/System/ScansAttachments.php index 0225cf0008..0e9b8c8128 100644 --- a/app/Console/Commands/System/ScansAttachments.php +++ b/app/Console/Commands/System/ScansAttachments.php @@ -34,7 +34,6 @@ use Illuminate\Support\Facades\Storage; use Safe\Exceptions\FileinfoException; use Safe\Exceptions\FilesystemException; use Safe\Exceptions\StringsException; - use function Safe\file_put_contents; use function Safe\md5_file; use function Safe\mime_content_type; diff --git a/app/Console/Commands/System/VerifySecurityAlerts.php b/app/Console/Commands/System/VerifySecurityAlerts.php index ee739ef9a3..878f762711 100644 --- a/app/Console/Commands/System/VerifySecurityAlerts.php +++ b/app/Console/Commands/System/VerifySecurityAlerts.php @@ -30,7 +30,6 @@ use Illuminate\Database\QueryException; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; use League\Flysystem\FilesystemException; - use Safe\Exceptions\JsonException; use function Safe\json_decode; diff --git a/app/Console/Commands/Upgrade/AddsTransactionIdentifiers.php b/app/Console/Commands/Upgrade/AddsTransactionIdentifiers.php index 4ffa9a2981..79a8f0bfaf 100644 --- a/app/Console/Commands/Upgrade/AddsTransactionIdentifiers.php +++ b/app/Console/Commands/Upgrade/AddsTransactionIdentifiers.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Upgrade; use FireflyIII\Console\Commands\ShowsFriendlyMessages; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Journal\JournalCLIRepositoryInterface; diff --git a/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php b/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php index 22b335f8d2..cf66e8cc06 100644 --- a/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php +++ b/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php @@ -34,7 +34,6 @@ use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; use JsonException; use stdClass; - use function Safe\json_decode; class RemovesDatabaseDecryption extends Command diff --git a/app/Console/Commands/Upgrade/UpgradesAccountCurrencies.php b/app/Console/Commands/Upgrade/UpgradesAccountCurrencies.php index c67aae9280..50a9fc71c1 100644 --- a/app/Console/Commands/Upgrade/UpgradesAccountCurrencies.php +++ b/app/Console/Commands/Upgrade/UpgradesAccountCurrencies.php @@ -24,14 +24,13 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Upgrade; -use FireflyIII\Models\TransactionJournal; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Enums\AccountTypeEnum; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Models\AccountMeta; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; diff --git a/app/Console/Commands/Upgrade/UpgradesDatabase.php b/app/Console/Commands/Upgrade/UpgradesDatabase.php index c2f6912b5a..bbc6c8ee68 100644 --- a/app/Console/Commands/Upgrade/UpgradesDatabase.php +++ b/app/Console/Commands/Upgrade/UpgradesDatabase.php @@ -24,10 +24,11 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Upgrade; +use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Support\Facades\FireflyConfig; +use Illuminate\Console\Command; use Illuminate\Support\Facades\Log; use Safe\Exceptions\InfoException; - use function Safe\set_time_limit; try { @@ -36,9 +37,6 @@ try { Log::warning('set_time_limit returned false. This could be an issue, unless you also run XDebug.'); } -use FireflyIII\Console\Commands\ShowsFriendlyMessages; -use Illuminate\Console\Command; - class UpgradesDatabase extends Command { use ShowsFriendlyMessages; diff --git a/app/Console/Commands/Upgrade/UpgradesRecurrenceMetaData.php b/app/Console/Commands/Upgrade/UpgradesRecurrenceMetaData.php index 82dcb3d160..7ffd859065 100644 --- a/app/Console/Commands/Upgrade/UpgradesRecurrenceMetaData.php +++ b/app/Console/Commands/Upgrade/UpgradesRecurrenceMetaData.php @@ -29,7 +29,6 @@ use FireflyIII\Models\Recurrence; use FireflyIII\Models\RecurrenceMeta; use FireflyIII\Models\RecurrenceTransactionMeta; use Illuminate\Console\Command; - use function Safe\json_encode; class UpgradesRecurrenceMetaData extends Command diff --git a/app/Console/Commands/Upgrade/UpgradesToGroups.php b/app/Console/Commands/Upgrade/UpgradesToGroups.php index 875df04cc4..95fbcf339b 100644 --- a/app/Console/Commands/Upgrade/UpgradesToGroups.php +++ b/app/Console/Commands/Upgrade/UpgradesToGroups.php @@ -25,6 +25,7 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Upgrade; use Carbon\Carbon; +use Exception; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Factory\TransactionGroupFactory; use FireflyIII\Models\Budget; @@ -37,7 +38,6 @@ use FireflyIII\Services\Internal\Destroy\JournalDestroyService; use Illuminate\Console\Command; use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; -use Exception; use Illuminate\Support\Facades\Log; class UpgradesToGroups extends Command diff --git a/app/Exceptions/GracefulNotFoundHandler.php b/app/Exceptions/GracefulNotFoundHandler.php index 762da63c55..6f905adb22 100644 --- a/app/Exceptions/GracefulNotFoundHandler.php +++ b/app/Exceptions/GracefulNotFoundHandler.php @@ -34,8 +34,8 @@ use FireflyIII\User; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; -use Symfony\Component\HttpFoundation\Response; use Override; +use Symfony\Component\HttpFoundation\Response; use Throwable; /** diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 3bde465581..f160030059 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -24,8 +24,9 @@ declare(strict_types=1); namespace FireflyIII\Exceptions; -use Carbon\Carbon; use Brick\Math\Exception\NumberFormatException; +use Carbon\Carbon; +use ErrorException; use FireflyIII\Jobs\MailError; use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Auth\AuthenticationException; @@ -40,16 +41,14 @@ use Illuminate\Support\Facades\Log; use Illuminate\Validation\ValidationException as LaravelValidationException; use Laravel\Passport\Exceptions\OAuthServerException as LaravelOAuthException; use League\OAuth2\Server\Exception\OAuthServerException; +use Override; use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use ErrorException; -use Override; use Throwable; - use function Safe\json_encode; use function Safe\parse_url; diff --git a/app/Exceptions/IntervalException.php b/app/Exceptions/IntervalException.php index 45fe7cddfb..4b6396ad19 100644 --- a/app/Exceptions/IntervalException.php +++ b/app/Exceptions/IntervalException.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Exceptions; -use FireflyIII\Support\Calendar\Periodicity; use Exception; +use FireflyIII\Support\Calendar\Periodicity; use Throwable; /** diff --git a/app/Factory/BillFactory.php b/app/Factory/BillFactory.php index 1fe4567134..fb284ae921 100644 --- a/app/Factory/BillFactory.php +++ b/app/Factory/BillFactory.php @@ -24,9 +24,9 @@ declare(strict_types=1); namespace FireflyIII\Factory; -use FireflyIII\Models\ObjectGroup; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Bill; +use FireflyIII\Models\ObjectGroup; use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups; use FireflyIII\Services\Internal\Support\BillServiceTrait; use FireflyIII\User; diff --git a/app/Factory/PiggyBankFactory.php b/app/Factory/PiggyBankFactory.php index b31056b10f..2cf4d35a68 100644 --- a/app/Factory/PiggyBankFactory.php +++ b/app/Factory/PiggyBankFactory.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Factory; -use FireflyIII\Models\ObjectGroup; -use FireflyIII\Models\Account; use FireflyIII\Events\Model\PiggyBank\ChangedAmount; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Account; +use FireflyIII\Models\ObjectGroup; use FireflyIII\Models\PiggyBank; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; @@ -36,7 +36,6 @@ use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\User; use Illuminate\Database\QueryException; use Illuminate\Support\Facades\Log; - use function Safe\json_encode; /** diff --git a/app/Factory/TransactionJournalFactory.php b/app/Factory/TransactionJournalFactory.php index 1c7f354c34..5f218c5d3f 100644 --- a/app/Factory/TransactionJournalFactory.php +++ b/app/Factory/TransactionJournalFactory.php @@ -24,15 +24,16 @@ declare(strict_types=1); namespace FireflyIII\Factory; -use FireflyIII\Models\Bill; -use FireflyIII\Models\PiggyBank; use Carbon\Carbon; +use Exception; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Exceptions\DuplicateTransactionException; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; +use FireflyIII\Models\Bill; use FireflyIII\Models\Location; +use FireflyIII\Models\PiggyBank; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionJournal; @@ -53,9 +54,7 @@ use FireflyIII\User; use FireflyIII\Validation\AccountValidator; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; -use Exception; use JsonException; - use function Safe\json_encode; /** diff --git a/app/Handlers/Events/APIEventHandler.php b/app/Handlers/Events/APIEventHandler.php index 7d59a4b636..de67afd8e8 100644 --- a/app/Handlers/Events/APIEventHandler.php +++ b/app/Handlers/Events/APIEventHandler.php @@ -24,11 +24,11 @@ declare(strict_types=1); namespace FireflyIII\Handlers\Events; +use Exception; use FireflyIII\Notifications\User\NewAccessToken; use FireflyIII\Repositories\User\UserRepositoryInterface; use Illuminate\Support\Facades\Notification; use Laravel\Passport\Events\AccessTokenCreated; -use Exception; /** * Class APIEventHandler diff --git a/app/Handlers/Events/AdminEventHandler.php b/app/Handlers/Events/AdminEventHandler.php index 6619763806..d3a9a1e73e 100644 --- a/app/Handlers/Events/AdminEventHandler.php +++ b/app/Handlers/Events/AdminEventHandler.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Handlers\Events; +use Exception; use FireflyIII\Events\Admin\InvitationCreated; use FireflyIII\Events\NewVersionAvailable; use FireflyIII\Events\Security\UnknownUserAttemptedLogin; @@ -36,7 +37,6 @@ use FireflyIII\Notifications\Test\OwnerTestNotificationPushover; use FireflyIII\Notifications\Test\OwnerTestNotificationSlack; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Notification; -use Exception; /** * Class AdminEventHandler. diff --git a/app/Handlers/Events/AutomationHandler.php b/app/Handlers/Events/AutomationHandler.php index 11b11a7b65..7a59a78a10 100644 --- a/app/Handlers/Events/AutomationHandler.php +++ b/app/Handlers/Events/AutomationHandler.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Handlers\Events; +use Exception; use FireflyIII\Events\RequestedReportOnJournals; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionGroup; @@ -31,7 +32,6 @@ use FireflyIII\Notifications\User\TransactionCreation; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Transformers\TransactionGroupTransformer; use Illuminate\Support\Facades\Notification; -use Exception; /** * Class AutomationHandler diff --git a/app/Handlers/Events/BillEventHandler.php b/app/Handlers/Events/BillEventHandler.php index 9969bfbf1c..aef9cb49d2 100644 --- a/app/Handlers/Events/BillEventHandler.php +++ b/app/Handlers/Events/BillEventHandler.php @@ -33,7 +33,6 @@ use FireflyIII\Notifications\User\SubscriptionsOverdueReminder; use FireflyIII\Support\Facades\Preferences; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Notification; - use function Safe\json_encode; /** diff --git a/app/Handlers/Events/Model/PiggyBankEventHandler.php b/app/Handlers/Events/Model/PiggyBankEventHandler.php index cea67d76b0..b44c9e4741 100644 --- a/app/Handlers/Events/Model/PiggyBankEventHandler.php +++ b/app/Handlers/Events/Model/PiggyBankEventHandler.php @@ -24,13 +24,13 @@ declare(strict_types=1); namespace FireflyIII\Handlers\Events\Model; +use FireflyIII\Events\Model\PiggyBank\ChangedAmount; use FireflyIII\Events\Model\PiggyBank\ChangedName; use FireflyIII\Models\Account; +use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionGroup; -use FireflyIII\Events\Model\PiggyBank\ChangedAmount; -use FireflyIII\Models\PiggyBankEvent; /** * Class PiggyBankEventHandler diff --git a/app/Handlers/Events/Security/MFAHandler.php b/app/Handlers/Events/Security/MFAHandler.php index a259a742e2..f3b6dab533 100644 --- a/app/Handlers/Events/Security/MFAHandler.php +++ b/app/Handlers/Events/Security/MFAHandler.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Handlers\Events\Security; +use Exception; use FireflyIII\Events\Security\DisabledMFA; use FireflyIII\Events\Security\EnabledMFA; use FireflyIII\Events\Security\MFABackupFewLeft; @@ -39,7 +40,6 @@ use FireflyIII\Notifications\Security\MFAManyFailedAttemptsNotification; use FireflyIII\Notifications\Security\MFAUsedBackupCodeNotification; use FireflyIII\Notifications\Security\NewBackupCodesNotification; use Illuminate\Support\Facades\Notification; -use Exception; class MFAHandler { diff --git a/app/Handlers/Events/UserEventHandler.php b/app/Handlers/Events/UserEventHandler.php index 775cdc878e..39271d86a2 100644 --- a/app/Handlers/Events/UserEventHandler.php +++ b/app/Handlers/Events/UserEventHandler.php @@ -25,6 +25,7 @@ namespace FireflyIII\Handlers\Events; use Carbon\Carbon; use Database\Seeders\ExchangeRateSeeder; +use Exception; use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Events\ActuallyLoggedIn; use FireflyIII\Events\Admin\InvitationCreated; @@ -55,7 +56,6 @@ use Illuminate\Auth\Events\Login; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Notification; -use Exception; /** * Class UserEventHandler. diff --git a/app/Handlers/Events/VersionCheckEventHandler.php b/app/Handlers/Events/VersionCheckEventHandler.php index 1d663855b0..2087d37f76 100644 --- a/app/Handlers/Events/VersionCheckEventHandler.php +++ b/app/Handlers/Events/VersionCheckEventHandler.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace FireflyIII\Handlers\Events; -use Deprecated; use Carbon\Carbon; +use Deprecated; use FireflyIII\Events\RequestedVersionCheckStatus; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Update\UpdateTrait; diff --git a/app/Helpers/Attachments/AttachmentHelper.php b/app/Helpers/Attachments/AttachmentHelper.php index 846be0c312..ef0cf558d2 100644 --- a/app/Helpers/Attachments/AttachmentHelper.php +++ b/app/Helpers/Attachments/AttachmentHelper.php @@ -27,7 +27,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Attachment; use FireflyIII\Models\PiggyBank; use Illuminate\Contracts\Encryption\DecryptException; -use Illuminate\Contracts\Encryption\EncryptException; use Illuminate\Contracts\Filesystem\Filesystem; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; @@ -39,13 +38,11 @@ use Safe\Exceptions\FileinfoException; use Safe\Exceptions\FilesystemException; use Safe\Exceptions\StringsException; use Symfony\Component\HttpFoundation\File\UploadedFile; - -use function Safe\tmpfile; -use function Safe\fwrite; -use function Safe\finfo_open; use function Safe\fclose; +use function Safe\finfo_open; +use function Safe\fwrite; use function Safe\md5_file; - +use function Safe\tmpfile; use const DIRECTORY_SEPARATOR; /** diff --git a/app/Helpers/Collector/Extensions/MetaCollection.php b/app/Helpers/Collector/Extensions/MetaCollection.php index 11a15ef799..40185ef81c 100644 --- a/app/Helpers/Collector/Extensions/MetaCollection.php +++ b/app/Helpers/Collector/Extensions/MetaCollection.php @@ -36,7 +36,6 @@ use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; - use function Safe\json_encode; /** diff --git a/app/Helpers/Collector/GroupCollector.php b/app/Helpers/Collector/GroupCollector.php index 0dd40ee445..db4834fbf2 100644 --- a/app/Helpers/Collector/GroupCollector.php +++ b/app/Helpers/Collector/GroupCollector.php @@ -47,7 +47,6 @@ use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use Override; - use Safe\Exceptions\JsonException; use function Safe\json_decode; diff --git a/app/Helpers/Report/NetWorth.php b/app/Helpers/Report/NetWorth.php index bb58b8761b..0ea1ce6cfe 100644 --- a/app/Helpers/Report/NetWorth.php +++ b/app/Helpers/Report/NetWorth.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Helpers\Report; -use Deprecated; use Carbon\Carbon; +use Deprecated; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; diff --git a/app/Helpers/Report/NetWorthInterface.php b/app/Helpers/Report/NetWorthInterface.php index 466a055a4c..421caa9a74 100644 --- a/app/Helpers/Report/NetWorthInterface.php +++ b/app/Helpers/Report/NetWorthInterface.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Helpers\Report; -use Deprecated; use Carbon\Carbon; +use Deprecated; use FireflyIII\Models\UserGroup; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; diff --git a/app/Helpers/Webhook/Sha3SignatureGenerator.php b/app/Helpers/Webhook/Sha3SignatureGenerator.php index 06967cff04..ce6a0680f7 100644 --- a/app/Helpers/Webhook/Sha3SignatureGenerator.php +++ b/app/Helpers/Webhook/Sha3SignatureGenerator.php @@ -28,7 +28,6 @@ use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\WebhookMessage; use JsonException; - use function Safe\json_encode; /** diff --git a/app/Http/Controllers/Account/CreateController.php b/app/Http/Controllers/Account/CreateController.php index e0cb55afc7..e884c7c5cc 100644 --- a/app/Http/Controllers/Account/CreateController.php +++ b/app/Http/Controllers/Account/CreateController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Account; use FireflyIII\Enums\AccountTypeEnum; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Attachments\AttachmentHelperInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\AccountFormRequest; diff --git a/app/Http/Controllers/Account/EditController.php b/app/Http/Controllers/Account/EditController.php index 7d172bead1..8c2d083b13 100644 --- a/app/Http/Controllers/Account/EditController.php +++ b/app/Http/Controllers/Account/EditController.php @@ -24,11 +24,11 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Account; -use FireflyIII\Models\Location; use FireflyIII\Helpers\Attachments\AttachmentHelperInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\AccountFormRequest; use FireflyIII\Models\Account; +use FireflyIII\Models\Location; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\Http\Controllers\ModelInformation; use Illuminate\Contracts\View\Factory; diff --git a/app/Http/Controllers/Account/IndexController.php b/app/Http/Controllers/Account/IndexController.php index 66844d8ad2..8d35f6b56e 100644 --- a/app/Http/Controllers/Account/IndexController.php +++ b/app/Http/Controllers/Account/IndexController.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Account; use Carbon\Carbon; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Account; use FireflyIII\Repositories\Account\AccountRepositoryInterface; diff --git a/app/Http/Controllers/AttachmentController.php b/app/Http/Controllers/AttachmentController.php index b06fc9a007..2741d94812 100644 --- a/app/Http/Controllers/AttachmentController.php +++ b/app/Http/Controllers/AttachmentController.php @@ -27,7 +27,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Requests\AttachmentFormRequest; use FireflyIII\Models\Attachment; use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface; -use Illuminate\Contracts\Container\BindingResolutionException; use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php index 1b3607259c..c9d54c0982 100644 --- a/app/Http/Controllers/Auth/ForgotPasswordController.php +++ b/app/Http/Controllers/Auth/ForgotPasswordController.php @@ -36,7 +36,6 @@ use Illuminate\View\View; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Safe\Exceptions\UrlException; - use function Safe\parse_url; /** diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 01b25bbd19..6e4bba5669 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -24,8 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Auth; use Carbon\Carbon; -use FireflyIII\User; -use Illuminate\Support\Facades\Cookie; use FireflyIII\Events\ActuallyLoggedIn; use FireflyIII\Events\Security\UnknownUserAttemptedLogin; use FireflyIII\Events\Security\UserAttemptedLogin; @@ -33,6 +31,7 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Providers\RouteServiceProvider; use FireflyIII\Repositories\User\UserRepositoryInterface; +use FireflyIII\User; use Illuminate\Contracts\Foundation\Application; use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\View; @@ -43,6 +42,7 @@ use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Routing\Redirector; +use Illuminate\Support\Facades\Cookie; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; use Illuminate\Validation\ValidationException; diff --git a/app/Http/Controllers/Bill/IndexController.php b/app/Http/Controllers/Bill/IndexController.php index 1725bad953..3896db77af 100644 --- a/app/Http/Controllers/Bill/IndexController.php +++ b/app/Http/Controllers/Bill/IndexController.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Bill; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Bill; use FireflyIII\Repositories\Bill\BillRepositoryInterface; diff --git a/app/Http/Controllers/Budget/EditController.php b/app/Http/Controllers/Budget/EditController.php index a3969e6d7d..6b0f820e20 100644 --- a/app/Http/Controllers/Budget/EditController.php +++ b/app/Http/Controllers/Budget/EditController.php @@ -24,11 +24,11 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Budget; -use FireflyIII\Models\AutoBudget; use FireflyIII\Enums\AutoBudgetType; use FireflyIII\Helpers\Attachments\AttachmentHelperInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\BudgetFormUpdateRequest; +use FireflyIII\Models\AutoBudget; use FireflyIII\Models\Budget; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use Illuminate\Contracts\View\Factory; diff --git a/app/Http/Controllers/Budget/ShowController.php b/app/Http/Controllers/Budget/ShowController.php index c28e7455e8..77158fb699 100644 --- a/app/Http/Controllers/Budget/ShowController.php +++ b/app/Http/Controllers/Budget/ShowController.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Budget; -use FireflyIII\Models\TransactionJournal; use Carbon\Carbon; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Exceptions\FireflyException; @@ -32,6 +31,7 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; +use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Support\Http\Controllers\AugumentData; diff --git a/app/Http/Controllers/Category/NoCategoryController.php b/app/Http/Controllers/Category/NoCategoryController.php index 609f351a46..065214b856 100644 --- a/app/Http/Controllers/Category/NoCategoryController.php +++ b/app/Http/Controllers/Category/NoCategoryController.php @@ -24,12 +24,12 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Category; -use FireflyIII\Models\TransactionJournal; use Carbon\Carbon; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Support\Http\Controllers\PeriodOverview; use Illuminate\Contracts\View\Factory; diff --git a/app/Http/Controllers/Chart/AccountController.php b/app/Http/Controllers/Chart/AccountController.php index 632589163f..9e0e34851c 100644 --- a/app/Http/Controllers/Chart/AccountController.php +++ b/app/Http/Controllers/Chart/AccountController.php @@ -44,7 +44,6 @@ use FireflyIII\Support\Http\Controllers\DateCalculation; use Illuminate\Http\JsonResponse; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; - use Safe\Exceptions\JsonException; use function Safe\json_encode; diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index d79700c4a2..1d3665b548 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Chart; use Carbon\Carbon; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Generator\Chart\Basic\GeneratorInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Category; diff --git a/app/Http/Controllers/Chart/PiggyBankController.php b/app/Http/Controllers/Chart/PiggyBankController.php index 1321de9eea..065cc16f49 100644 --- a/app/Http/Controllers/Chart/PiggyBankController.php +++ b/app/Http/Controllers/Chart/PiggyBankController.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Chart; use Carbon\Carbon; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Generator\Chart\Basic\GeneratorInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\PiggyBank; diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 0919f8c199..daba3dbeae 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -36,11 +36,10 @@ use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Routing\Controller as BaseController; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Log; -use Illuminate\Support\Facades\View; use Illuminate\Support\Facades\Route; - -use function Safe\realpath; +use Illuminate\Support\Facades\View; use function Safe\ini_get; +use function Safe\realpath; /** * Class Controller. diff --git a/app/Http/Controllers/DebugController.php b/app/Http/Controllers/DebugController.php index 1ed00b4710..f83dfb0f37 100644 --- a/app/Http/Controllers/DebugController.php +++ b/app/Http/Controllers/DebugController.php @@ -51,10 +51,8 @@ use Illuminate\View\View; use Monolog\Handler\RotatingFileHandler; use Safe\Exceptions\FilesystemException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; - use function Safe\file_get_contents; use function Safe\ini_get; - use const PHP_INT_SIZE; use const PHP_SAPI; diff --git a/app/Http/Controllers/Export/IndexController.php b/app/Http/Controllers/Export/IndexController.php index 3167f65525..3089c4ac35 100644 --- a/app/Http/Controllers/Export/IndexController.php +++ b/app/Http/Controllers/Export/IndexController.php @@ -25,10 +25,10 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Export; use Carbon\Carbon; -use FireflyIII\Models\TransactionJournal; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Middleware\IsDemoUser; +use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Support\Export\ExportDataGenerator; use Illuminate\Contracts\View\Factory; diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 3ab394545e..e72c691d41 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -23,9 +23,9 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers; -use Exception; use Carbon\Carbon; use Carbon\Exceptions\InvalidFormatException; +use Exception; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Events\RequestedVersionCheckStatus; use FireflyIII\Exceptions\FireflyException; diff --git a/app/Http/Controllers/Json/BoxController.php b/app/Http/Controllers/Json/BoxController.php index cd845df963..d202a80af0 100644 --- a/app/Http/Controllers/Json/BoxController.php +++ b/app/Http/Controllers/Json/BoxController.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Json; -use Deprecated; use Carbon\Carbon; +use Deprecated; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Helpers\Collector\GroupCollectorInterface; diff --git a/app/Http/Controllers/Json/BudgetController.php b/app/Http/Controllers/Json/BudgetController.php index 23a687ed46..6a0b494194 100644 --- a/app/Http/Controllers/Json/BudgetController.php +++ b/app/Http/Controllers/Json/BudgetController.php @@ -24,9 +24,9 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Json; -use FireflyIII\Models\AvailableBudget; use Carbon\Carbon; use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Models\AvailableBudget; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; diff --git a/app/Http/Controllers/Json/FrontpageController.php b/app/Http/Controllers/Json/FrontpageController.php index f66864866b..a51499cfc2 100644 --- a/app/Http/Controllers/Json/FrontpageController.php +++ b/app/Http/Controllers/Json/FrontpageController.php @@ -23,12 +23,12 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Json; -use Throwable; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\PiggyBank; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use Illuminate\Http\JsonResponse; +use Throwable; /** * Class FrontpageController. diff --git a/app/Http/Controllers/Json/IntroController.php b/app/Http/Controllers/Json/IntroController.php index a08c32b6be..8d1aa947a8 100644 --- a/app/Http/Controllers/Json/IntroController.php +++ b/app/Http/Controllers/Json/IntroController.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Json; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Support\Http\Controllers\GetConfigurationData; use Illuminate\Http\JsonResponse; diff --git a/app/Http/Controllers/Json/ReconcileController.php b/app/Http/Controllers/Json/ReconcileController.php index a1208182e8..3d88bb0a88 100644 --- a/app/Http/Controllers/Json/ReconcileController.php +++ b/app/Http/Controllers/Json/ReconcileController.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Json; -use Throwable; use Carbon\Carbon; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Exceptions\FireflyException; @@ -38,6 +37,7 @@ use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; +use Throwable; /** * Class ReconcileController diff --git a/app/Http/Controllers/Json/RuleController.php b/app/Http/Controllers/Json/RuleController.php index a555015150..1826cd0a80 100644 --- a/app/Http/Controllers/Json/RuleController.php +++ b/app/Http/Controllers/Json/RuleController.php @@ -24,11 +24,11 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Json; -use Throwable; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Throwable; /** * Class RuleController diff --git a/app/Http/Controllers/NewUserController.php b/app/Http/Controllers/NewUserController.php index 8350216064..e1c70c78f4 100644 --- a/app/Http/Controllers/NewUserController.php +++ b/app/Http/Controllers/NewUserController.php @@ -23,10 +23,9 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers; -use FireflyIII\Models\TransactionCurrency; use FireflyIII\Enums\AccountTypeEnum; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Requests\NewUserFormRequest; +use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\Http\Controllers\CreateStuff; diff --git a/app/Http/Controllers/PiggyBank/IndexController.php b/app/Http/Controllers/PiggyBank/IndexController.php index 69bbd38780..199d90a3a8 100644 --- a/app/Http/Controllers/PiggyBank/IndexController.php +++ b/app/Http/Controllers/PiggyBank/IndexController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\PiggyBank; use Carbon\Carbon; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Account; use FireflyIII\Models\PiggyBank; diff --git a/app/Http/Controllers/PreferencesController.php b/app/Http/Controllers/PreferencesController.php index 7a22b91c06..723998cd3f 100644 --- a/app/Http/Controllers/PreferencesController.php +++ b/app/Http/Controllers/PreferencesController.php @@ -23,8 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers; -use FireflyIII\Support\Singleton\PreferencesSingleton; -use JsonException; use Carbon\Carbon; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Events\Preferences\UserGroupChangedPrimaryCurrency; @@ -35,6 +33,7 @@ use FireflyIII\Models\Account; use FireflyIII\Models\Preference; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\Facades\Preferences; +use FireflyIII\Support\Singleton\PreferencesSingleton; use FireflyIII\User; use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; @@ -42,10 +41,10 @@ use Illuminate\Http\Request; use Illuminate\Routing\Redirector; use Illuminate\Support\Facades\Log; use Illuminate\View\View; - +use JsonException; use Safe\Exceptions\FilesystemException; -use function Safe\json_decode; use function Safe\file_get_contents; +use function Safe\json_decode; /** * Class PreferencesController. diff --git a/app/Http/Controllers/Profile/MfaController.php b/app/Http/Controllers/Profile/MfaController.php index 69537bd12f..7959f1cd54 100644 --- a/app/Http/Controllers/Profile/MfaController.php +++ b/app/Http/Controllers/Profile/MfaController.php @@ -24,13 +24,10 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Profile; -use Illuminate\Support\Facades\Cookie; -use PragmaRX\Google2FALaravel\Facade as Google2FA; use Carbon\Carbon; use FireflyIII\Events\Security\DisabledMFA; use FireflyIII\Events\Security\EnabledMFA; use FireflyIII\Events\Security\MFANewBackupCodes; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Middleware\IsDemoUser; use FireflyIII\Http\Requests\ExistingTokenFormRequest; @@ -41,8 +38,10 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Routing\Redirector; +use Illuminate\Support\Facades\Cookie; use Illuminate\Support\Facades\Log; use Illuminate\View\View; +use PragmaRX\Google2FALaravel\Facade as Google2FA; use PragmaRX\Recovery\Recovery; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 7eb2e745cf..33f8b41866 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -24,8 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers; use Exception; -use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Hash; use FireflyIII\Events\UserChangedEmail; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\ValidationException; @@ -45,7 +43,9 @@ use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Routing\Redirector; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Hash; use Illuminate\View\View; use Laravel\Passport\ClientRepository; use Psr\Container\ContainerExceptionInterface; diff --git a/app/Http/Controllers/Report/AccountController.php b/app/Http/Controllers/Report/AccountController.php index dcd9464ce3..c6818cd9c6 100644 --- a/app/Http/Controllers/Report/AccountController.php +++ b/app/Http/Controllers/Report/AccountController.php @@ -23,13 +23,13 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Report; -use Throwable; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; +use Throwable; /** * Class AccountController. diff --git a/app/Http/Controllers/Report/BalanceController.php b/app/Http/Controllers/Report/BalanceController.php index 41b3010a4a..3764008f2a 100644 --- a/app/Http/Controllers/Report/BalanceController.php +++ b/app/Http/Controllers/Report/BalanceController.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Report; -use Throwable; use Carbon\Carbon; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Exceptions\FireflyException; @@ -33,6 +32,7 @@ use FireflyIII\Models\Account; use FireflyIII\Models\Budget; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use Illuminate\Support\Collection; +use Throwable; /** * Class BalanceController. diff --git a/app/Http/Controllers/Report/BillController.php b/app/Http/Controllers/Report/BillController.php index 1fd70e3c48..9cea9f5edd 100644 --- a/app/Http/Controllers/Report/BillController.php +++ b/app/Http/Controllers/Report/BillController.php @@ -24,13 +24,13 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Report; -use Throwable; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Report\ReportHelperInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; +use Throwable; /** * Class BillController diff --git a/app/Http/Controllers/Report/BudgetController.php b/app/Http/Controllers/Report/BudgetController.php index a59076ab9a..29c802bb96 100644 --- a/app/Http/Controllers/Report/BudgetController.php +++ b/app/Http/Controllers/Report/BudgetController.php @@ -23,8 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Report; -use FireflyIII\Support\Facades\Navigation; -use Throwable; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; @@ -32,11 +30,13 @@ use FireflyIII\Models\Account; use FireflyIII\Models\Budget; use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; use FireflyIII\Support\CacheProperties; +use FireflyIII\Support\Facades\Navigation; use FireflyIII\Support\Http\Controllers\BasicDataSupport; use FireflyIII\Support\Report\Budget\BudgetReportGenerator; use Illuminate\Contracts\View\Factory; use Illuminate\Support\Collection; use Illuminate\View\View; +use Throwable; /** * Class BudgetController. diff --git a/app/Http/Controllers/Report/CategoryController.php b/app/Http/Controllers/Report/CategoryController.php index fc8d17421a..1ae30d59e8 100644 --- a/app/Http/Controllers/Report/CategoryController.php +++ b/app/Http/Controllers/Report/CategoryController.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Report; -use Throwable; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; @@ -37,6 +36,7 @@ use FireflyIII\Support\Report\Category\CategoryReportGenerator; use Illuminate\Contracts\View\Factory; use Illuminate\Support\Collection; use Illuminate\View\View; +use Throwable; /** * Class CategoryController. diff --git a/app/Http/Controllers/Report/DoubleController.php b/app/Http/Controllers/Report/DoubleController.php index 32a1736844..ffe0545273 100644 --- a/app/Http/Controllers/Report/DoubleController.php +++ b/app/Http/Controllers/Report/DoubleController.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Report; -use Throwable; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; @@ -34,6 +33,7 @@ use FireflyIII\Support\Http\Controllers\AugumentData; use Illuminate\Contracts\View\Factory; use Illuminate\Support\Collection; use Illuminate\View\View; +use Throwable; /** * Class DoubleController diff --git a/app/Http/Controllers/Report/OperationsController.php b/app/Http/Controllers/Report/OperationsController.php index d2aaeaa3dd..e4f10fc574 100644 --- a/app/Http/Controllers/Report/OperationsController.php +++ b/app/Http/Controllers/Report/OperationsController.php @@ -23,13 +23,13 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Report; -use Throwable; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; +use Throwable; /** * Class OperationsController. diff --git a/app/Http/Controllers/Report/TagController.php b/app/Http/Controllers/Report/TagController.php index 22bca3fd99..4eaeb46e90 100644 --- a/app/Http/Controllers/Report/TagController.php +++ b/app/Http/Controllers/Report/TagController.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Report; -use Throwable; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; @@ -34,6 +33,7 @@ use FireflyIII\Repositories\Tag\OperationsRepositoryInterface; use Illuminate\Contracts\View\Factory; use Illuminate\Support\Collection; use Illuminate\View\View; +use Throwable; /** * Class TagController diff --git a/app/Http/Controllers/Rule/CreateController.php b/app/Http/Controllers/Rule/CreateController.php index 107042d458..24c9c8bd64 100644 --- a/app/Http/Controllers/Rule/CreateController.php +++ b/app/Http/Controllers/Rule/CreateController.php @@ -24,11 +24,11 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Rule; -use FireflyIII\Models\Rule; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\RuleFormRequest; use FireflyIII\Models\Bill; +use FireflyIII\Models\Rule; use FireflyIII\Models\RuleGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Rule\RuleRepositoryInterface; diff --git a/app/Http/Controllers/Rule/EditController.php b/app/Http/Controllers/Rule/EditController.php index 848fa259a3..f1888c425d 100644 --- a/app/Http/Controllers/Rule/EditController.php +++ b/app/Http/Controllers/Rule/EditController.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Rule; -use Throwable; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\RuleFormRequest; @@ -39,6 +38,7 @@ use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Routing\Redirector; use Illuminate\View\View; +use Throwable; /** * Class EditController diff --git a/app/Http/Controllers/Rule/SelectController.php b/app/Http/Controllers/Rule/SelectController.php index 4d85f8ae3d..1084a9f2c8 100644 --- a/app/Http/Controllers/Rule/SelectController.php +++ b/app/Http/Controllers/Rule/SelectController.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Rule; -use Throwable; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\SelectTransactionsRequest; @@ -39,6 +38,7 @@ use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; use Illuminate\Support\Collection; use Illuminate\View\View; +use Throwable; /** * Class SelectController. diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index b30d1eefad..39fa3cea10 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers; -use Throwable; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Repositories\Rule\RuleRepositoryInterface; use FireflyIII\Support\Search\SearchInterface; @@ -31,6 +30,7 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\View\View; +use Throwable; /** * Class SearchController. diff --git a/app/Http/Controllers/System/InstallController.php b/app/Http/Controllers/System/InstallController.php index cd800053d4..48ff8a64b0 100644 --- a/app/Http/Controllers/System/InstallController.php +++ b/app/Http/Controllers/System/InstallController.php @@ -24,20 +24,19 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\System; -use FireflyIII\Support\Facades\FireflyConfig; -use Illuminate\Support\Facades\Artisan; -use Illuminate\Support\Facades\Cache; use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Support\Facades\FireflyConfig; use FireflyIII\Support\Http\Controllers\GetConfigurationData; use Illuminate\Contracts\View\Factory; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Artisan; +use Illuminate\Support\Facades\Cache; use Illuminate\View\View; use Laravel\Passport\Passport; use phpseclib3\Crypt\RSA; - use function Safe\file_put_contents; /** diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index 0fd2ef0336..d062801d47 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -23,12 +23,12 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers; -use FireflyIII\Models\Location; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Attachments\AttachmentHelperInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Http\Requests\TagFormRequest; +use FireflyIII\Models\Location; use FireflyIII\Models\Tag; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Support\Http\Controllers\PeriodOverview; diff --git a/app/Http/Controllers/Transaction/ConvertController.php b/app/Http/Controllers/Transaction/ConvertController.php index ec211a9b0d..bb5aa6f7d6 100644 --- a/app/Http/Controllers/Transaction/ConvertController.php +++ b/app/Http/Controllers/Transaction/ConvertController.php @@ -23,8 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Transaction; -use FireflyIII\Models\Transaction; -use FireflyIII\Models\TransactionCurrency; use Exception; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Enums\TransactionTypeEnum; @@ -32,6 +30,8 @@ use FireflyIII\Events\UpdatedTransactionGroup; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Account; +use FireflyIII\Models\Transaction; +use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; diff --git a/app/Http/Controllers/Transaction/CreateController.php b/app/Http/Controllers/Transaction/CreateController.php index 1fc030e5e9..71012d3565 100644 --- a/app/Http/Controllers/Transaction/CreateController.php +++ b/app/Http/Controllers/Transaction/CreateController.php @@ -24,10 +24,9 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Transaction; -use FireflyIII\Models\TransactionGroup; use FireflyIII\Events\StoredTransactionGroup; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Models\TransactionGroup; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface; use FireflyIII\Services\Internal\Update\GroupCloneService; @@ -36,7 +35,6 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\View; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; - use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Safe\Exceptions\UrlException; diff --git a/app/Http/Controllers/Transaction/EditController.php b/app/Http/Controllers/Transaction/EditController.php index 2fa41b6de9..dcf9e36ca3 100644 --- a/app/Http/Controllers/Transaction/EditController.php +++ b/app/Http/Controllers/Transaction/EditController.php @@ -34,7 +34,6 @@ use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; use Illuminate\Routing\Redirector; use Illuminate\View\View; - use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Safe\Exceptions\UrlException; diff --git a/app/Http/Controllers/Transaction/IndexController.php b/app/Http/Controllers/Transaction/IndexController.php index 92f67137c6..e0d2c83359 100644 --- a/app/Http/Controllers/Transaction/IndexController.php +++ b/app/Http/Controllers/Transaction/IndexController.php @@ -24,11 +24,11 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Transaction; -use FireflyIII\Models\TransactionJournal; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Support\Http\Controllers\PeriodOverview; use Illuminate\Contracts\View\Factory; diff --git a/app/Http/Controllers/Transaction/MassController.php b/app/Http/Controllers/Transaction/MassController.php index bb7f4609ae..7fde459a35 100644 --- a/app/Http/Controllers/Transaction/MassController.php +++ b/app/Http/Controllers/Transaction/MassController.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Transaction; -use InvalidArgumentException; use Carbon\Carbon; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Enums\TransactionTypeEnum; @@ -42,6 +41,7 @@ use Illuminate\Http\RedirectResponse; use Illuminate\Routing\Redirector; use Illuminate\Support\Facades\Log; use Illuminate\View\View as IlluminateView; +use InvalidArgumentException; /** * Class MassController. diff --git a/app/Http/Controllers/TransactionCurrency/CreateController.php b/app/Http/Controllers/TransactionCurrency/CreateController.php index 2e7372b213..89febb82b5 100644 --- a/app/Http/Controllers/TransactionCurrency/CreateController.php +++ b/app/Http/Controllers/TransactionCurrency/CreateController.php @@ -24,10 +24,10 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\TransactionCurrency; -use FireflyIII\Models\TransactionCurrency; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\CurrencyFormRequest; +use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; diff --git a/app/Http/Middleware/AcceptHeaders.php b/app/Http/Middleware/AcceptHeaders.php index d8884aebf3..c8b8749ad8 100644 --- a/app/Http/Middleware/AcceptHeaders.php +++ b/app/Http/Middleware/AcceptHeaders.php @@ -29,7 +29,6 @@ use Illuminate\Http\Request; use Illuminate\Http\Response; use Safe\Exceptions\PcreException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; - use function Safe\preg_match; class AcceptHeaders diff --git a/app/Http/Middleware/Range.php b/app/Http/Middleware/Range.php index a9e7a705cd..47bcf126e2 100644 --- a/app/Http/Middleware/Range.php +++ b/app/Http/Middleware/Range.php @@ -23,14 +23,14 @@ declare(strict_types=1); namespace FireflyIII\Http\Middleware; -use Closure; -use FireflyIII\Support\Facades\Steam; -use Illuminate\Support\Facades\App; use Carbon\Carbon; +use Closure; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Support\Facades\Amount; +use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Http\Controllers\RequestInformation; use Illuminate\Http\Request; +use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Log; /** diff --git a/app/Http/Middleware/SecureHeaders.php b/app/Http/Middleware/SecureHeaders.php index 5f8b104992..246a9bf4a3 100644 --- a/app/Http/Middleware/SecureHeaders.php +++ b/app/Http/Middleware/SecureHeaders.php @@ -24,9 +24,9 @@ declare(strict_types=1); namespace FireflyIII\Http\Middleware; +use Barryvdh\Debugbar\Facades\Debugbar; use Closure; use Exception; -use Barryvdh\Debugbar\Facades\Debugbar; use Illuminate\Http\Request; use Illuminate\Support\Facades\Vite; diff --git a/app/Http/Middleware/StartFireflySession.php b/app/Http/Middleware/StartFireflySession.php index 922bc3ad8c..02aab37b34 100644 --- a/app/Http/Middleware/StartFireflySession.php +++ b/app/Http/Middleware/StartFireflySession.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Http\Middleware; -use Override; use Illuminate\Contracts\Session\Session; use Illuminate\Http\Request; use Illuminate\Session\Middleware\StartSession; +use Override; /** * Class StartFireflySession. diff --git a/app/Http/Middleware/TrustHosts.php b/app/Http/Middleware/TrustHosts.php index 4446201045..e41086a8db 100644 --- a/app/Http/Middleware/TrustHosts.php +++ b/app/Http/Middleware/TrustHosts.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Http\Middleware; -use Override; use Illuminate\Http\Middleware\TrustHosts as Middleware; +use Override; class TrustHosts extends Middleware { diff --git a/app/Http/Requests/AccountFormRequest.php b/app/Http/Requests/AccountFormRequest.php index ced11547e0..9cd8d51163 100644 --- a/app/Http/Requests/AccountFormRequest.php +++ b/app/Http/Requests/AccountFormRequest.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\Account; use FireflyIII\Models\Location; @@ -34,6 +33,7 @@ use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class AccountFormRequest. diff --git a/app/Http/Requests/AttachmentFormRequest.php b/app/Http/Requests/AttachmentFormRequest.php index f2b4beb568..f481510ee6 100644 --- a/app/Http/Requests/AttachmentFormRequest.php +++ b/app/Http/Requests/AttachmentFormRequest.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class AttachmentFormRequest. diff --git a/app/Http/Requests/BillStoreRequest.php b/app/Http/Requests/BillStoreRequest.php index 46436a7c9d..37e9f7ef02 100644 --- a/app/Http/Requests/BillStoreRequest.php +++ b/app/Http/Requests/BillStoreRequest.php @@ -23,12 +23,12 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class BillStoreRequest. diff --git a/app/Http/Requests/BillUpdateRequest.php b/app/Http/Requests/BillUpdateRequest.php index 2f70b26df3..cde6978364 100644 --- a/app/Http/Requests/BillUpdateRequest.php +++ b/app/Http/Requests/BillUpdateRequest.php @@ -23,13 +23,13 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Models\Bill; use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class BillUpdateRequest. diff --git a/app/Http/Requests/BudgetFormStoreRequest.php b/app/Http/Requests/BudgetFormStoreRequest.php index ac4edf6e69..86779d3794 100644 --- a/app/Http/Requests/BudgetFormStoreRequest.php +++ b/app/Http/Requests/BudgetFormStoreRequest.php @@ -23,13 +23,13 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Validation\AutoBudget\ValidatesAutoBudgetRequest; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class BudgetFormStoreRequest diff --git a/app/Http/Requests/BudgetFormUpdateRequest.php b/app/Http/Requests/BudgetFormUpdateRequest.php index 539e613fff..cdebd759e7 100644 --- a/app/Http/Requests/BudgetFormUpdateRequest.php +++ b/app/Http/Requests/BudgetFormUpdateRequest.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Models\Budget; use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; @@ -31,6 +30,7 @@ use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Validation\AutoBudget\ValidatesAutoBudgetRequest; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class BudgetFormUpdateRequest diff --git a/app/Http/Requests/BudgetIncomeRequest.php b/app/Http/Requests/BudgetIncomeRequest.php index 49ed18de14..d75f81faa9 100644 --- a/app/Http/Requests/BudgetIncomeRequest.php +++ b/app/Http/Requests/BudgetIncomeRequest.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class BudgetIncomeRequest. diff --git a/app/Http/Requests/BulkEditJournalRequest.php b/app/Http/Requests/BulkEditJournalRequest.php index 169d1bc6ce..4bbaafa77b 100644 --- a/app/Http/Requests/BulkEditJournalRequest.php +++ b/app/Http/Requests/BulkEditJournalRequest.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class MassEditBulkJournalRequest. diff --git a/app/Http/Requests/CategoryFormRequest.php b/app/Http/Requests/CategoryFormRequest.php index a4e5112669..8ca3455168 100644 --- a/app/Http/Requests/CategoryFormRequest.php +++ b/app/Http/Requests/CategoryFormRequest.php @@ -23,12 +23,12 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Models\Category; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class CategoryFormRequest. diff --git a/app/Http/Requests/ConfigurationRequest.php b/app/Http/Requests/ConfigurationRequest.php index 712d2be5a4..57f8a969b4 100644 --- a/app/Http/Requests/ConfigurationRequest.php +++ b/app/Http/Requests/ConfigurationRequest.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class ConfigurationRequest. diff --git a/app/Http/Requests/CurrencyFormRequest.php b/app/Http/Requests/CurrencyFormRequest.php index 60ce09cbba..959678e64c 100644 --- a/app/Http/Requests/CurrencyFormRequest.php +++ b/app/Http/Requests/CurrencyFormRequest.php @@ -23,12 +23,12 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class CurrencyFormRequest. diff --git a/app/Http/Requests/DeleteAccountFormRequest.php b/app/Http/Requests/DeleteAccountFormRequest.php index eb36622b3a..31222e084a 100644 --- a/app/Http/Requests/DeleteAccountFormRequest.php +++ b/app/Http/Requests/DeleteAccountFormRequest.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class DeleteAccountFormRequest. diff --git a/app/Http/Requests/EmailFormRequest.php b/app/Http/Requests/EmailFormRequest.php index 1f499245eb..cd4369f3ec 100644 --- a/app/Http/Requests/EmailFormRequest.php +++ b/app/Http/Requests/EmailFormRequest.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class EmailFormRequest. diff --git a/app/Http/Requests/ExistingTokenFormRequest.php b/app/Http/Requests/ExistingTokenFormRequest.php index f3222a8d57..b843012fba 100644 --- a/app/Http/Requests/ExistingTokenFormRequest.php +++ b/app/Http/Requests/ExistingTokenFormRequest.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class ExistingTokenFormRequest. diff --git a/app/Http/Requests/InviteUserFormRequest.php b/app/Http/Requests/InviteUserFormRequest.php index 70ebad1f19..2c31d965ce 100644 --- a/app/Http/Requests/InviteUserFormRequest.php +++ b/app/Http/Requests/InviteUserFormRequest.php @@ -24,11 +24,11 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class InviteUserFormRequest diff --git a/app/Http/Requests/JournalLinkRequest.php b/app/Http/Requests/JournalLinkRequest.php index 487b47f91c..185aaea191 100644 --- a/app/Http/Requests/JournalLinkRequest.php +++ b/app/Http/Requests/JournalLinkRequest.php @@ -23,12 +23,12 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Models\LinkType; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class JournalLink. diff --git a/app/Http/Requests/LinkTypeFormRequest.php b/app/Http/Requests/LinkTypeFormRequest.php index bc0a56c933..3fb7511c6a 100644 --- a/app/Http/Requests/LinkTypeFormRequest.php +++ b/app/Http/Requests/LinkTypeFormRequest.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class LinkTypeFormRequest. diff --git a/app/Http/Requests/MassDeleteJournalRequest.php b/app/Http/Requests/MassDeleteJournalRequest.php index fc1c5e93f8..603e8a7d51 100644 --- a/app/Http/Requests/MassDeleteJournalRequest.php +++ b/app/Http/Requests/MassDeleteJournalRequest.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class MassDeleteJournalRequest. diff --git a/app/Http/Requests/MassEditJournalRequest.php b/app/Http/Requests/MassEditJournalRequest.php index ccf00f8988..7f217aaf67 100644 --- a/app/Http/Requests/MassEditJournalRequest.php +++ b/app/Http/Requests/MassEditJournalRequest.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class MassEditJournalRequest. diff --git a/app/Http/Requests/NewUserFormRequest.php b/app/Http/Requests/NewUserFormRequest.php index 967d7fe461..bed1e3def7 100644 --- a/app/Http/Requests/NewUserFormRequest.php +++ b/app/Http/Requests/NewUserFormRequest.php @@ -23,12 +23,12 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Rules\IsValidAmount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class NewUserFormRequest. diff --git a/app/Http/Requests/ObjectGroupFormRequest.php b/app/Http/Requests/ObjectGroupFormRequest.php index ccc0cf24f5..8d8dcb1b8a 100644 --- a/app/Http/Requests/ObjectGroupFormRequest.php +++ b/app/Http/Requests/ObjectGroupFormRequest.php @@ -23,12 +23,12 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Models\ObjectGroup; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class ObjectGroupFormRequest. diff --git a/app/Http/Requests/PiggyBankStoreRequest.php b/app/Http/Requests/PiggyBankStoreRequest.php index 868b1cc4bd..87a13140da 100644 --- a/app/Http/Requests/PiggyBankStoreRequest.php +++ b/app/Http/Requests/PiggyBankStoreRequest.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; @@ -33,6 +32,7 @@ use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class PiggyBankStoreRequest. diff --git a/app/Http/Requests/PiggyBankUpdateRequest.php b/app/Http/Requests/PiggyBankUpdateRequest.php index e0ecaf9614..a93fcada2f 100644 --- a/app/Http/Requests/PiggyBankUpdateRequest.php +++ b/app/Http/Requests/PiggyBankUpdateRequest.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\PiggyBank; use FireflyIII\Models\TransactionCurrency; @@ -34,6 +33,7 @@ use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class PiggyBankFormRequest. diff --git a/app/Http/Requests/ProfileFormRequest.php b/app/Http/Requests/ProfileFormRequest.php index 1a7956d647..bf741778e6 100644 --- a/app/Http/Requests/ProfileFormRequest.php +++ b/app/Http/Requests/ProfileFormRequest.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class ProfileFormRequest. diff --git a/app/Http/Requests/ReconciliationStoreRequest.php b/app/Http/Requests/ReconciliationStoreRequest.php index e8c8b1a9be..75a1a6d8e3 100644 --- a/app/Http/Requests/ReconciliationStoreRequest.php +++ b/app/Http/Requests/ReconciliationStoreRequest.php @@ -24,13 +24,13 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Rules\IsValidAmount; use FireflyIII\Rules\ValidJournals; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class ReconciliationStoreRequest diff --git a/app/Http/Requests/RecurrenceFormRequest.php b/app/Http/Requests/RecurrenceFormRequest.php index a4f9014078..d7340dd793 100644 --- a/app/Http/Requests/RecurrenceFormRequest.php +++ b/app/Http/Requests/RecurrenceFormRequest.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Factory\CategoryFactory; @@ -37,6 +36,7 @@ use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Validation\AccountValidator; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class RecurrenceFormRequest diff --git a/app/Http/Requests/ReportFormRequest.php b/app/Http/Requests/ReportFormRequest.php index 0061ca310a..fd11a32096 100644 --- a/app/Http/Requests/ReportFormRequest.php +++ b/app/Http/Requests/ReportFormRequest.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use Carbon\Carbon; use Exception; use FireflyIII\Exceptions\FireflyException; @@ -35,7 +34,7 @@ use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; - +use Illuminate\Validation\Validator; use Safe\Exceptions\PcreException; use function Safe\preg_match; diff --git a/app/Http/Requests/RuleFormRequest.php b/app/Http/Requests/RuleFormRequest.php index f47f4b7c3f..8b7d9ca061 100644 --- a/app/Http/Requests/RuleFormRequest.php +++ b/app/Http/Requests/RuleFormRequest.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Models\Rule; use FireflyIII\Rules\IsValidActionExpression; use FireflyIII\Support\Request\ChecksLogin; @@ -31,6 +30,7 @@ use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\GetRuleConfiguration; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class RuleFormRequest. diff --git a/app/Http/Requests/RuleGroupFormRequest.php b/app/Http/Requests/RuleGroupFormRequest.php index 6ebc1a6233..8f5c4af01b 100644 --- a/app/Http/Requests/RuleGroupFormRequest.php +++ b/app/Http/Requests/RuleGroupFormRequest.php @@ -23,13 +23,13 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Models\RuleGroup; use FireflyIII\Rules\IsBoolean; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class RuleGroupFormRequest. diff --git a/app/Http/Requests/SelectTransactionsRequest.php b/app/Http/Requests/SelectTransactionsRequest.php index 6b27652abc..fdd9e0ecf8 100644 --- a/app/Http/Requests/SelectTransactionsRequest.php +++ b/app/Http/Requests/SelectTransactionsRequest.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class SelectTransactionsRequest. diff --git a/app/Http/Requests/TagFormRequest.php b/app/Http/Requests/TagFormRequest.php index 28a733b78c..a156257b78 100644 --- a/app/Http/Requests/TagFormRequest.php +++ b/app/Http/Requests/TagFormRequest.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Models\Location; use FireflyIII\Models\Tag; use FireflyIII\Support\Request\AppendsLocationData; @@ -31,6 +30,7 @@ use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class TagFormRequest. diff --git a/app/Http/Requests/TestRuleFormRequest.php b/app/Http/Requests/TestRuleFormRequest.php index 880c45625e..0cf0524667 100644 --- a/app/Http/Requests/TestRuleFormRequest.php +++ b/app/Http/Requests/TestRuleFormRequest.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\GetRuleConfiguration; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class TestRuleFormRequest. diff --git a/app/Http/Requests/TokenFormRequest.php b/app/Http/Requests/TokenFormRequest.php index a54a1e786b..3a53cbe7ab 100644 --- a/app/Http/Requests/TokenFormRequest.php +++ b/app/Http/Requests/TokenFormRequest.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class TokenFormRequest. diff --git a/app/Http/Requests/TriggerRecurrenceRequest.php b/app/Http/Requests/TriggerRecurrenceRequest.php index 4b929adaa7..c03e531a00 100644 --- a/app/Http/Requests/TriggerRecurrenceRequest.php +++ b/app/Http/Requests/TriggerRecurrenceRequest.php @@ -24,11 +24,11 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class TriggerRecurrenceRequest diff --git a/app/Http/Requests/UserFormRequest.php b/app/Http/Requests/UserFormRequest.php index b53be02934..409cfba453 100644 --- a/app/Http/Requests/UserFormRequest.php +++ b/app/Http/Requests/UserFormRequest.php @@ -23,11 +23,11 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class UserFormRequest. diff --git a/app/Http/Requests/UserRegistrationRequest.php b/app/Http/Requests/UserRegistrationRequest.php index 64df7a021b..6e26763831 100644 --- a/app/Http/Requests/UserRegistrationRequest.php +++ b/app/Http/Requests/UserRegistrationRequest.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Class UserRegistrationRequest. diff --git a/app/Jobs/DownloadExchangeRates.php b/app/Jobs/DownloadExchangeRates.php index 1bbb3ee10e..9b29bcaac5 100644 --- a/app/Jobs/DownloadExchangeRates.php +++ b/app/Jobs/DownloadExchangeRates.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Jobs; -use FireflyIII\Models\CurrencyExchangeRate; use Carbon\Carbon; +use FireflyIII\Models\CurrencyExchangeRate; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface; @@ -40,7 +40,6 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; - use Safe\Exceptions\JsonException; use function Safe\json_decode; diff --git a/app/Jobs/MailError.php b/app/Jobs/MailError.php index 57cfaf7feb..70c7f407b4 100644 --- a/app/Jobs/MailError.php +++ b/app/Jobs/MailError.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Jobs; use Carbon\Carbon; +use Exception; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Message; use Illuminate\Queue\InteractsWithQueue; @@ -31,12 +32,10 @@ use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Mail; use Symfony\Component\Mailer\Exception\TransportException; -use Exception; - -use function Safe\json_encode; +use function Safe\file_get_contents; use function Safe\file_put_contents; use function Safe\json_decode; -use function Safe\file_get_contents; +use function Safe\json_encode; /** * Class MailError. diff --git a/app/Mail/InvitationMail.php b/app/Mail/InvitationMail.php index 14ad8821b5..0c886bc21e 100644 --- a/app/Mail/InvitationMail.php +++ b/app/Mail/InvitationMail.php @@ -27,7 +27,6 @@ namespace FireflyIII\Mail; use Illuminate\Bus\Queueable; use Illuminate\Mail\Mailable; use Illuminate\Queue\SerializesModels; - use function Safe\parse_url; /** diff --git a/app/Models/AccountMeta.php b/app/Models/AccountMeta.php index a91e5eb778..ff42cb44f7 100644 --- a/app/Models/AccountMeta.php +++ b/app/Models/AccountMeta.php @@ -27,7 +27,6 @@ use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; - use function Safe\json_decode; use function Safe\json_encode; diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index 3fd2d82a19..e1fe1be20f 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -27,7 +27,6 @@ use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; - use function Safe\json_decode; use function Safe\json_encode; diff --git a/app/Models/TransactionJournalMeta.php b/app/Models/TransactionJournalMeta.php index 4a748bfb8b..8c2c02ad72 100644 --- a/app/Models/TransactionJournalMeta.php +++ b/app/Models/TransactionJournalMeta.php @@ -28,7 +28,6 @@ use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; - use function Safe\json_decode; use function Safe\json_encode; diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 263342b753..e28f83d871 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -30,7 +30,6 @@ use Illuminate\Support\Facades\Schema; use Illuminate\Support\ServiceProvider; use Laravel\Passport\Passport; use Override; - use function Safe\preg_match; /** diff --git a/app/Providers/FireflyServiceProvider.php b/app/Providers/FireflyServiceProvider.php index e900b72f9e..cb9bebbbf4 100644 --- a/app/Providers/FireflyServiceProvider.php +++ b/app/Providers/FireflyServiceProvider.php @@ -77,8 +77,8 @@ use FireflyIII\Validation\FireflyValidator; use Illuminate\Foundation\Application; use Illuminate\Support\Facades\Validator; use Illuminate\Support\ServiceProvider; -use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Override; +use Symfony\Component\ExpressionLanguage\ExpressionLanguage; /** * Class FireflyServiceProvider. diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index 8eacb438b4..4b0278de7d 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -48,7 +48,6 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; use Override; - use function Safe\json_encode; /** diff --git a/app/Repositories/Attachment/AttachmentRepository.php b/app/Repositories/Attachment/AttachmentRepository.php index 0e41c59cf4..cd0d3ea249 100644 --- a/app/Repositories/Attachment/AttachmentRepository.php +++ b/app/Repositories/Attachment/AttachmentRepository.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Attachment; +use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Factory\AttachmentFactory; use FireflyIII\Helpers\Attachments\AttachmentHelperInterface; @@ -35,7 +36,6 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\Crypt; use Illuminate\Support\Facades\Storage; use League\Flysystem\UnableToDeleteFile; -use Exception; use LogicException; /** diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index 99c484f4a8..daa88a5a65 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -23,13 +23,13 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Bill; -use FireflyIII\Models\ObjectGroup; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Factory\BillFactory; use FireflyIII\Models\Attachment; use FireflyIII\Models\Bill; use FireflyIII\Models\Note; +use FireflyIII\Models\ObjectGroup; use FireflyIII\Models\Rule; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; diff --git a/app/Repositories/Budget/AvailableBudgetRepository.php b/app/Repositories/Budget/AvailableBudgetRepository.php index ee9db0ca62..cc2916cb92 100644 --- a/app/Repositories/Budget/AvailableBudgetRepository.php +++ b/app/Repositories/Budget/AvailableBudgetRepository.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Budget; -use Deprecated; use Carbon\Carbon; +use Deprecated; use FireflyIII\Models\AvailableBudget; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Support\Facades\Amount; diff --git a/app/Repositories/Budget/AvailableBudgetRepositoryInterface.php b/app/Repositories/Budget/AvailableBudgetRepositoryInterface.php index 3d54e3f20b..46251e79d2 100644 --- a/app/Repositories/Budget/AvailableBudgetRepositoryInterface.php +++ b/app/Repositories/Budget/AvailableBudgetRepositoryInterface.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Budget; -use Deprecated; use Carbon\Carbon; +use Deprecated; use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\AvailableBudget; use FireflyIII\Models\TransactionCurrency; diff --git a/app/Repositories/Budget/NoBudgetRepositoryInterface.php b/app/Repositories/Budget/NoBudgetRepositoryInterface.php index 01b42c3396..13d12a1122 100644 --- a/app/Repositories/Budget/NoBudgetRepositoryInterface.php +++ b/app/Repositories/Budget/NoBudgetRepositoryInterface.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Budget; -use Deprecated; use Carbon\Carbon; +use Deprecated; use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\UserGroup; diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 6856e3552a..7e46ff30d9 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Category; use Carbon\Carbon; +use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Factory\CategoryFactory; use FireflyIII\Models\Attachment; @@ -39,7 +40,6 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; -use Exception; /** * Class CategoryRepository. diff --git a/app/Repositories/Currency/CurrencyRepository.php b/app/Repositories/Currency/CurrencyRepository.php index c76b5badfb..a3f673f108 100644 --- a/app/Repositories/Currency/CurrencyRepository.php +++ b/app/Repositories/Currency/CurrencyRepository.php @@ -44,7 +44,6 @@ use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use Override; - use Safe\Exceptions\JsonException; use function Safe\json_encode; diff --git a/app/Repositories/PiggyBank/PiggyBankRepository.php b/app/Repositories/PiggyBank/PiggyBankRepository.php index 8ca1f14723..056825e7d8 100644 --- a/app/Repositories/PiggyBank/PiggyBankRepository.php +++ b/app/Repositories/PiggyBank/PiggyBankRepository.php @@ -23,9 +23,8 @@ declare(strict_types=1); namespace FireflyIII\Repositories\PiggyBank; -use FireflyIII\Events\Model\PiggyBank\ChangedAmount; -use FireflyIII\User; use Carbon\Carbon; +use FireflyIII\Events\Model\PiggyBank\ChangedAmount; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Factory\PiggyBankFactory; use FireflyIII\Models\Account; @@ -40,6 +39,7 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; +use FireflyIII\User; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; diff --git a/app/Repositories/Recurring/RecurringRepository.php b/app/Repositories/Recurring/RecurringRepository.php index f131b3d1f6..c519ab397f 100644 --- a/app/Repositories/Recurring/RecurringRepository.php +++ b/app/Repositories/Recurring/RecurringRepository.php @@ -50,9 +50,8 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; - -use function Safe\json_encode; use function Safe\json_decode; +use function Safe\json_encode; /** * Class RecurringRepository diff --git a/app/Repositories/Rule/RuleRepository.php b/app/Repositories/Rule/RuleRepository.php index 5774e84e9e..e8435dc6c2 100644 --- a/app/Repositories/Rule/RuleRepository.php +++ b/app/Repositories/Rule/RuleRepository.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Rule; +use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleAction; @@ -33,7 +34,6 @@ use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use FireflyIII\Support\Search\OperatorQuerySearch; use Illuminate\Support\Collection; -use Exception; /** * Class RuleRepository. diff --git a/app/Repositories/RuleGroup/RuleGroupRepository.php b/app/Repositories/RuleGroup/RuleGroupRepository.php index 3e1694baee..a3256ccf50 100644 --- a/app/Repositories/RuleGroup/RuleGroupRepository.php +++ b/app/Repositories/RuleGroup/RuleGroupRepository.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Repositories\RuleGroup; +use Exception; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleGroup; @@ -32,7 +33,6 @@ use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; -use Exception; /** * Class RuleGroupRepository. diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index eb50e2ef8f..b900e9143a 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Tag; -use Override; use Carbon\Carbon; +use Exception; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Factory\TagFactory; use FireflyIII\Helpers\Collector\GroupCollectorInterface; @@ -39,7 +39,7 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; -use Exception; +use Override; /** * Class TagRepository. diff --git a/app/Repositories/TransactionGroup/TransactionGroupRepository.php b/app/Repositories/TransactionGroup/TransactionGroupRepository.php index 78acf6d253..8e8eac95e3 100644 --- a/app/Repositories/TransactionGroup/TransactionGroupRepository.php +++ b/app/Repositories/TransactionGroup/TransactionGroupRepository.php @@ -49,7 +49,6 @@ use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; - use function Safe\json_decode; /** diff --git a/app/Repositories/User/UserRepository.php b/app/Repositories/User/UserRepository.php index e00b15ba9d..d2e5c3fa81 100644 --- a/app/Repositories/User/UserRepository.php +++ b/app/Repositories/User/UserRepository.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Repositories\User; use Carbon\Carbon; +use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\GroupMembership; @@ -35,7 +36,6 @@ use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\QueryException; use Illuminate\Support\Collection; use Illuminate\Support\Str; -use Exception; use Override; /** diff --git a/app/Rules/Account/IsValidAccountType.php b/app/Rules/Account/IsValidAccountType.php index d2287ff702..d2ac0603cf 100644 --- a/app/Rules/Account/IsValidAccountType.php +++ b/app/Rules/Account/IsValidAccountType.php @@ -24,9 +24,9 @@ declare(strict_types=1); namespace FireflyIII\Rules\Account; +use Closure; use FireflyIII\Support\Http\Api\AccountFilter; use Illuminate\Contracts\Validation\ValidationRule; -use Closure; use Override; class IsValidAccountType implements ValidationRule diff --git a/app/Rules/Admin/IsValidDiscordUrl.php b/app/Rules/Admin/IsValidDiscordUrl.php index ccb4800935..a45bb12a4f 100644 --- a/app/Rules/Admin/IsValidDiscordUrl.php +++ b/app/Rules/Admin/IsValidDiscordUrl.php @@ -25,10 +25,10 @@ declare(strict_types=1); namespace FireflyIII\Rules\Admin; +use Closure; use FireflyIII\Support\Validation\ValidatesAmountsTrait; use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Support\Facades\Log; -use Closure; class IsValidDiscordUrl implements ValidationRule { diff --git a/app/Rules/Admin/IsValidSlackOrDiscordUrl.php b/app/Rules/Admin/IsValidSlackOrDiscordUrl.php index 5b9f6063da..8efbf682c9 100644 --- a/app/Rules/Admin/IsValidSlackOrDiscordUrl.php +++ b/app/Rules/Admin/IsValidSlackOrDiscordUrl.php @@ -25,10 +25,10 @@ declare(strict_types=1); namespace FireflyIII\Rules\Admin; +use Closure; use FireflyIII\Support\Validation\ValidatesAmountsTrait; use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Support\Facades\Log; -use Closure; class IsValidSlackOrDiscordUrl implements ValidationRule { diff --git a/app/Rules/Admin/IsValidSlackUrl.php b/app/Rules/Admin/IsValidSlackUrl.php index 90fc1dafcd..0355b03f97 100644 --- a/app/Rules/Admin/IsValidSlackUrl.php +++ b/app/Rules/Admin/IsValidSlackUrl.php @@ -25,10 +25,10 @@ declare(strict_types=1); namespace FireflyIII\Rules\Admin; +use Closure; use FireflyIII\Support\Validation\ValidatesAmountsTrait; use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Support\Facades\Log; -use Closure; class IsValidSlackUrl implements ValidationRule { diff --git a/app/Rules/BelongsUser.php b/app/Rules/BelongsUser.php index 2e1fa3a9c2..48e44ddcbc 100644 --- a/app/Rules/BelongsUser.php +++ b/app/Rules/BelongsUser.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Rules; +use Closure; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Models\Bill; @@ -32,7 +33,6 @@ use FireflyIII\Models\Category; use FireflyIII\Models\PiggyBank; use FireflyIII\Models\TransactionJournal; use Illuminate\Contracts\Validation\ValidationRule; -use Closure; /** * Class BelongsUser diff --git a/app/Rules/BelongsUserGroup.php b/app/Rules/BelongsUserGroup.php index fe29b1fbef..488080a176 100644 --- a/app/Rules/BelongsUserGroup.php +++ b/app/Rules/BelongsUserGroup.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Rules; +use Closure; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Models\Bill; @@ -33,7 +34,6 @@ use FireflyIII\Models\PiggyBank; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\UserGroup; use Illuminate\Contracts\Validation\ValidationRule; -use Closure; /** * Class BelongsUserGroup diff --git a/app/Rules/IsAllowedGroupAction.php b/app/Rules/IsAllowedGroupAction.php index f3a197f113..7e30cd30ec 100644 --- a/app/Rules/IsAllowedGroupAction.php +++ b/app/Rules/IsAllowedGroupAction.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Rules; +use Closure; use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\Account; use FireflyIII\Repositories\UserGroup\UserGroupRepositoryInterface; @@ -31,7 +32,6 @@ use FireflyIII\User; use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Support\Facades\Log; -use Closure; use Override; class IsAllowedGroupAction implements ValidationRule diff --git a/app/Rules/IsAssetAccountId.php b/app/Rules/IsAssetAccountId.php index e8d26937fd..eadc485442 100644 --- a/app/Rules/IsAssetAccountId.php +++ b/app/Rules/IsAssetAccountId.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Rules; +use Closure; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Models\Account; use Illuminate\Contracts\Validation\ValidationRule; -use Closure; /** * Class IsAssetAccountId diff --git a/app/Rules/IsBoolean.php b/app/Rules/IsBoolean.php index 28c1ca3781..7c2b31c99f 100644 --- a/app/Rules/IsBoolean.php +++ b/app/Rules/IsBoolean.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Illuminate\Contracts\Validation\ValidationRule; use Closure; +use Illuminate\Contracts\Validation\ValidationRule; /** * Class IsBoolean diff --git a/app/Rules/IsDateOrTime.php b/app/Rules/IsDateOrTime.php index 64d711ec99..ae732d0ab3 100644 --- a/app/Rules/IsDateOrTime.php +++ b/app/Rules/IsDateOrTime.php @@ -27,8 +27,8 @@ namespace FireflyIII\Rules; use Carbon\Carbon; use Carbon\Exceptions\InvalidDateException; use Carbon\Exceptions\InvalidFormatException; -use Illuminate\Contracts\Validation\ValidationRule; use Closure; +use Illuminate\Contracts\Validation\ValidationRule; /** * Class IsDateOrTime diff --git a/app/Rules/IsDefaultUserGroupName.php b/app/Rules/IsDefaultUserGroupName.php index 81b366a2e6..f59ff49327 100644 --- a/app/Rules/IsDefaultUserGroupName.php +++ b/app/Rules/IsDefaultUserGroupName.php @@ -24,11 +24,11 @@ declare(strict_types=1); namespace FireflyIII\Rules; +use Closure; use FireflyIII\Models\UserGroup; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; use Illuminate\Contracts\Validation\ValidationRule; -use Closure; /** * Class IsDefaultUserGroupName diff --git a/app/Rules/IsDuplicateTransaction.php b/app/Rules/IsDuplicateTransaction.php index 1f9a125205..bceb22b95f 100644 --- a/app/Rules/IsDuplicateTransaction.php +++ b/app/Rules/IsDuplicateTransaction.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Illuminate\Contracts\Validation\ValidationRule; use Closure; +use Illuminate\Contracts\Validation\ValidationRule; /** * TODO not sure where this is used. diff --git a/app/Rules/IsFilterValueIn.php b/app/Rules/IsFilterValueIn.php index e167bdb79c..f7f41eb545 100644 --- a/app/Rules/IsFilterValueIn.php +++ b/app/Rules/IsFilterValueIn.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Illuminate\Contracts\Validation\ValidationRule; use Closure; +use Illuminate\Contracts\Validation\ValidationRule; class IsFilterValueIn implements ValidationRule { diff --git a/app/Rules/IsTransferAccount.php b/app/Rules/IsTransferAccount.php index 22f1fc5d8c..d64f30fa38 100644 --- a/app/Rules/IsTransferAccount.php +++ b/app/Rules/IsTransferAccount.php @@ -24,10 +24,10 @@ declare(strict_types=1); namespace FireflyIII\Rules; +use Closure; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Validation\AccountValidator; use Illuminate\Contracts\Validation\ValidationRule; -use Closure; /** * Class IsTransferAccount diff --git a/app/Rules/IsValidActionExpression.php b/app/Rules/IsValidActionExpression.php index d12b507bd3..f6080a0903 100644 --- a/app/Rules/IsValidActionExpression.php +++ b/app/Rules/IsValidActionExpression.php @@ -25,10 +25,10 @@ declare(strict_types=1); namespace FireflyIII\Rules; +use Closure; use FireflyIII\TransactionRules\Expressions\ActionExpression; use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Translation\PotentiallyTranslatedString; -use Closure; class IsValidActionExpression implements ValidationRule { diff --git a/app/Rules/IsValidAmount.php b/app/Rules/IsValidAmount.php index 208d60c696..26b06c8ac0 100644 --- a/app/Rules/IsValidAmount.php +++ b/app/Rules/IsValidAmount.php @@ -25,10 +25,10 @@ declare(strict_types=1); namespace FireflyIII\Rules; +use Closure; use FireflyIII\Support\Validation\ValidatesAmountsTrait; use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Support\Facades\Log; -use Closure; class IsValidAmount implements ValidationRule { diff --git a/app/Rules/IsValidAttachmentModel.php b/app/Rules/IsValidAttachmentModel.php index 093e650cf2..69d09b708c 100644 --- a/app/Rules/IsValidAttachmentModel.php +++ b/app/Rules/IsValidAttachmentModel.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Rules; +use Closure; use FireflyIII\Models\Account; use FireflyIII\Models\Bill; use FireflyIII\Models\Budget; @@ -41,7 +42,6 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use Illuminate\Contracts\Validation\ValidationRule; -use Closure; /** * Class IsValidAttachmentModel diff --git a/app/Rules/IsValidBulkClause.php b/app/Rules/IsValidBulkClause.php index 7d5ed57290..f3c74630b1 100644 --- a/app/Rules/IsValidBulkClause.php +++ b/app/Rules/IsValidBulkClause.php @@ -24,11 +24,10 @@ declare(strict_types=1); namespace FireflyIII\Rules; +use Closure; use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Support\Facades\Validator; -use Closure; use JsonException; - use function Safe\json_decode; /** diff --git a/app/Rules/IsValidDateRange.php b/app/Rules/IsValidDateRange.php index faf4b4343e..75a6846fab 100644 --- a/app/Rules/IsValidDateRange.php +++ b/app/Rules/IsValidDateRange.php @@ -27,8 +27,8 @@ namespace FireflyIII\Rules; use Carbon\Carbon; use Carbon\Exceptions\InvalidDateException; use Carbon\Exceptions\InvalidFormatException; -use Illuminate\Contracts\Validation\ValidationRule; use Closure; +use Illuminate\Contracts\Validation\ValidationRule; class IsValidDateRange implements ValidationRule { diff --git a/app/Rules/IsValidPositiveAmount.php b/app/Rules/IsValidPositiveAmount.php index 2c6eeb63cd..f637cdbc6d 100644 --- a/app/Rules/IsValidPositiveAmount.php +++ b/app/Rules/IsValidPositiveAmount.php @@ -25,11 +25,10 @@ declare(strict_types=1); namespace FireflyIII\Rules; +use Closure; use FireflyIII\Support\Validation\ValidatesAmountsTrait; use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Support\Facades\Log; -use Closure; - use function Safe\json_encode; class IsValidPositiveAmount implements ValidationRule diff --git a/app/Rules/IsValidZeroOrMoreAmount.php b/app/Rules/IsValidZeroOrMoreAmount.php index 3c93762333..f5880fa78a 100644 --- a/app/Rules/IsValidZeroOrMoreAmount.php +++ b/app/Rules/IsValidZeroOrMoreAmount.php @@ -25,10 +25,10 @@ declare(strict_types=1); namespace FireflyIII\Rules; +use Closure; use FireflyIII\Support\Validation\ValidatesAmountsTrait; use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Support\Facades\Log; -use Closure; class IsValidZeroOrMoreAmount implements ValidationRule { diff --git a/app/Rules/LessThanPiggyTarget.php b/app/Rules/LessThanPiggyTarget.php index 78df186875..3f34ab068a 100644 --- a/app/Rules/LessThanPiggyTarget.php +++ b/app/Rules/LessThanPiggyTarget.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Illuminate\Contracts\Validation\ValidationRule; use Closure; +use Illuminate\Contracts\Validation\ValidationRule; /** * Class LessThanPiggyTarget diff --git a/app/Rules/UniqueAccountNumber.php b/app/Rules/UniqueAccountNumber.php index 9cfae9d359..d605010d9d 100644 --- a/app/Rules/UniqueAccountNumber.php +++ b/app/Rules/UniqueAccountNumber.php @@ -24,12 +24,11 @@ declare(strict_types=1); namespace FireflyIII\Rules; +use Closure; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Models\Account; use FireflyIII\Models\AccountMeta; use Illuminate\Contracts\Validation\ValidationRule; -use Closure; - use function Safe\json_encode; /** diff --git a/app/Rules/UniqueIban.php b/app/Rules/UniqueIban.php index 9246326c15..3f83ab76bf 100644 --- a/app/Rules/UniqueIban.php +++ b/app/Rules/UniqueIban.php @@ -24,11 +24,11 @@ declare(strict_types=1); namespace FireflyIII\Rules; +use Closure; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Models\Account; use FireflyIII\Support\Facades\Steam; use Illuminate\Contracts\Validation\ValidationRule; -use Closure; /** * Class UniqueIban diff --git a/app/Rules/ValidJournals.php b/app/Rules/ValidJournals.php index 2e2b6c6230..221bef831d 100644 --- a/app/Rules/ValidJournals.php +++ b/app/Rules/ValidJournals.php @@ -24,9 +24,9 @@ declare(strict_types=1); namespace FireflyIII\Rules; +use Closure; use FireflyIII\Models\TransactionJournal; use Illuminate\Contracts\Validation\ValidationRule; -use Closure; /** * Class ValidJournals diff --git a/app/Rules/ValidRecurrenceRepetitionType.php b/app/Rules/ValidRecurrenceRepetitionType.php index f8e0fe3606..a9a923c892 100644 --- a/app/Rules/ValidRecurrenceRepetitionType.php +++ b/app/Rules/ValidRecurrenceRepetitionType.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Illuminate\Contracts\Validation\ValidationRule; use Closure; +use Illuminate\Contracts\Validation\ValidationRule; /** * Class ValidRecurrenceRepetitionType diff --git a/app/Rules/ValidRecurrenceRepetitionValue.php b/app/Rules/ValidRecurrenceRepetitionValue.php index 5a1a4b9031..9cd29b261b 100644 --- a/app/Rules/ValidRecurrenceRepetitionValue.php +++ b/app/Rules/ValidRecurrenceRepetitionValue.php @@ -25,8 +25,8 @@ declare(strict_types=1); namespace FireflyIII\Rules; use Carbon\Carbon; -use Illuminate\Contracts\Validation\ValidationRule; use Closure; +use Illuminate\Contracts\Validation\ValidationRule; use InvalidArgumentException; /** diff --git a/app/Services/FireflyIIIOrg/Update/UpdateRequest.php b/app/Services/FireflyIIIOrg/Update/UpdateRequest.php index ad55aee903..99ead72517 100644 --- a/app/Services/FireflyIIIOrg/Update/UpdateRequest.php +++ b/app/Services/FireflyIIIOrg/Update/UpdateRequest.php @@ -31,7 +31,6 @@ use GuzzleHttp\Client; use GuzzleHttp\Exception\GuzzleException; use Illuminate\Support\Facades\Log; use JsonException; - use function Safe\json_decode; /** diff --git a/app/Services/Internal/Support/AccountServiceTrait.php b/app/Services/Internal/Support/AccountServiceTrait.php index 5525181b97..cf2f0e4dc6 100644 --- a/app/Services/Internal/Support/AccountServiceTrait.php +++ b/app/Services/Internal/Support/AccountServiceTrait.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Services\Internal\Support; -use Deprecated; use Carbon\Carbon; +use Deprecated; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Exceptions\DuplicateTransactionException; use FireflyIII\Exceptions\FireflyException; diff --git a/app/Services/Internal/Support/JournalServiceTrait.php b/app/Services/Internal/Support/JournalServiceTrait.php index abe783365f..757ce01c0d 100644 --- a/app/Services/Internal/Support/JournalServiceTrait.php +++ b/app/Services/Internal/Support/JournalServiceTrait.php @@ -38,7 +38,6 @@ use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Rules\UniqueIban; use FireflyIII\Support\NullArrayObject; use Illuminate\Support\Facades\Log; - use Safe\Exceptions\JsonException; use function Safe\json_encode; diff --git a/app/Services/Internal/Support/RecurringTransactionTrait.php b/app/Services/Internal/Support/RecurringTransactionTrait.php index ebb7aad99f..b6f5955cb2 100644 --- a/app/Services/Internal/Support/RecurringTransactionTrait.php +++ b/app/Services/Internal/Support/RecurringTransactionTrait.php @@ -42,7 +42,6 @@ use FireflyIII\Models\RecurrenceTransactionMeta; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Validation\AccountValidator; use Illuminate\Support\Facades\Log; - use function Safe\json_encode; /** diff --git a/app/Services/Internal/Update/BillUpdateService.php b/app/Services/Internal/Update/BillUpdateService.php index d71d82a524..4fdd94aae0 100644 --- a/app/Services/Internal/Update/BillUpdateService.php +++ b/app/Services/Internal/Update/BillUpdateService.php @@ -24,10 +24,9 @@ declare(strict_types=1); namespace FireflyIII\Services\Internal\Update; -use FireflyIII\Models\ObjectGroup; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Factory\TransactionCurrencyFactory; use FireflyIII\Models\Bill; +use FireflyIII\Models\ObjectGroup; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleTrigger; use FireflyIII\Repositories\Bill\BillRepositoryInterface; diff --git a/app/Services/Internal/Update/CategoryUpdateService.php b/app/Services/Internal/Update/CategoryUpdateService.php index 193a5957b2..b04c06686e 100644 --- a/app/Services/Internal/Update/CategoryUpdateService.php +++ b/app/Services/Internal/Update/CategoryUpdateService.php @@ -24,13 +24,13 @@ declare(strict_types=1); namespace FireflyIII\Services\Internal\Update; +use Exception; use FireflyIII\Models\Category; use FireflyIII\Models\Note; use FireflyIII\Models\RecurrenceTransactionMeta; use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleTrigger; use FireflyIII\User; -use Exception; /** * Class CategoryUpdateService diff --git a/app/Services/Webhook/StandardWebhookSender.php b/app/Services/Webhook/StandardWebhookSender.php index ab9a82108a..e0f6cd5dfb 100644 --- a/app/Services/Webhook/StandardWebhookSender.php +++ b/app/Services/Webhook/StandardWebhookSender.php @@ -34,7 +34,6 @@ use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Exception\RequestException; use Illuminate\Support\Facades\Log; use JsonException; - use function Safe\json_encode; /** diff --git a/app/Support/Binder/CLIToken.php b/app/Support/Binder/CLIToken.php index b57ae19261..6dc825c585 100644 --- a/app/Support/Binder/CLIToken.php +++ b/app/Support/Binder/CLIToken.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Support\Binder; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Repositories\User\UserRepositoryInterface; use Illuminate\Routing\Route; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; diff --git a/app/Support/CacheProperties.php b/app/Support/CacheProperties.php index e22808ea06..258bea8495 100644 --- a/app/Support/CacheProperties.php +++ b/app/Support/CacheProperties.php @@ -27,7 +27,6 @@ use Carbon\Carbon; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Cache; use JsonException; - use function Safe\json_encode; /** diff --git a/app/Support/Http/Controllers/CreateStuff.php b/app/Support/Http/Controllers/CreateStuff.php index a69c44ac54..32e0c2c1a4 100644 --- a/app/Support/Http/Controllers/CreateStuff.php +++ b/app/Support/Http/Controllers/CreateStuff.php @@ -32,7 +32,6 @@ use FireflyIII\User; use Illuminate\Support\Facades\Log; use Laravel\Passport\Passport; use phpseclib3\Crypt\RSA; - use function Safe\file_put_contents; /** diff --git a/app/Support/Http/Controllers/RequestInformation.php b/app/Support/Http/Controllers/RequestInformation.php index 39a6df3aff..4f9702bb29 100644 --- a/app/Support/Http/Controllers/RequestInformation.php +++ b/app/Support/Http/Controllers/RequestInformation.php @@ -35,7 +35,6 @@ use Illuminate\Routing\Route; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Route as RouteFacade; use Illuminate\Support\Facades\Validator; - use function Safe\parse_url; /** diff --git a/app/Support/JsonApi/Enrichments/RecurringEnrichment.php b/app/Support/JsonApi/Enrichments/RecurringEnrichment.php index 1ea71f1dc4..25bb788c73 100644 --- a/app/Support/JsonApi/Enrichments/RecurringEnrichment.php +++ b/app/Support/JsonApi/Enrichments/RecurringEnrichment.php @@ -51,7 +51,6 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; - use function Safe\json_decode; class RecurringEnrichment implements EnrichmentInterface diff --git a/app/Support/ParseDateString.php b/app/Support/ParseDateString.php index 45404e1952..883e76be41 100644 --- a/app/Support/ParseDateString.php +++ b/app/Support/ParseDateString.php @@ -29,7 +29,6 @@ use Carbon\CarbonInterface; use Carbon\Exceptions\InvalidFormatException; use FireflyIII\Exceptions\FireflyException; use Illuminate\Support\Facades\Log; - use Safe\Exceptions\PcreException; use function Safe\preg_match; diff --git a/app/Support/Report/Budget/BudgetReportGenerator.php b/app/Support/Report/Budget/BudgetReportGenerator.php index 1bf9c81b3e..ecdc897160 100644 --- a/app/Support/Report/Budget/BudgetReportGenerator.php +++ b/app/Support/Report/Budget/BudgetReportGenerator.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Support\Report\Budget; use Carbon\Carbon; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; diff --git a/app/Support/Request/ConvertsDataTypes.php b/app/Support/Request/ConvertsDataTypes.php index 5293a6f804..c978f2785c 100644 --- a/app/Support/Request/ConvertsDataTypes.php +++ b/app/Support/Request/ConvertsDataTypes.php @@ -31,7 +31,6 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\Facades\Steam; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; - use function Safe\preg_replace; /** diff --git a/app/Support/Search/AccountSearch.php b/app/Support/Search/AccountSearch.php index 44bb34e893..9e54a772d4 100644 --- a/app/Support/Search/AccountSearch.php +++ b/app/Support/Search/AccountSearch.php @@ -28,7 +28,6 @@ use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Collection; - use function Safe\json_encode; /** diff --git a/app/Support/Search/QueryParser/GdbotsQueryParser.php b/app/Support/Search/QueryParser/GdbotsQueryParser.php index 127dc475b5..cc51218eaa 100644 --- a/app/Support/Search/QueryParser/GdbotsQueryParser.php +++ b/app/Support/Search/QueryParser/GdbotsQueryParser.php @@ -33,7 +33,6 @@ use Illuminate\Support\Facades\Log; use LogicException; use Safe\Exceptions\FilesystemException; use TypeError; - use function Safe\fwrite; class GdbotsQueryParser implements QueryParserInterface diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 9488fe36fb..f88839823d 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -40,7 +40,6 @@ use Illuminate\Support\Str; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use ValueError; - use function Safe\parse_url; use function Safe\preg_replace; diff --git a/app/Support/System/OAuthKeys.php b/app/Support/System/OAuthKeys.php index e4878d01cf..e39ed5c5de 100644 --- a/app/Support/System/OAuthKeys.php +++ b/app/Support/System/OAuthKeys.php @@ -31,7 +31,6 @@ use Illuminate\Support\Facades\Crypt; use Laravel\Passport\Console\KeysCommand; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; - use Safe\Exceptions\FilesystemException; use function Safe\file_get_contents; use function Safe\file_put_contents; diff --git a/app/Support/Twig/General.php b/app/Support/Twig/General.php index 485dfe1bd9..a3b0a3e128 100644 --- a/app/Support/Twig/General.php +++ b/app/Support/Twig/General.php @@ -37,7 +37,6 @@ use Override; use Twig\Extension\AbstractExtension; use Twig\TwigFilter; use Twig\TwigFunction; - use function Safe\parse_url; /** diff --git a/app/Support/Twig/TransactionGroupTwig.php b/app/Support/Twig/TransactionGroupTwig.php index 3033a84872..30b539f08a 100644 --- a/app/Support/Twig/TransactionGroupTwig.php +++ b/app/Support/Twig/TransactionGroupTwig.php @@ -34,7 +34,6 @@ use Illuminate\Support\Facades\DB; use Override; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; - use function Safe\json_decode; /** diff --git a/app/Transformers/PiggyBankTransformer.php b/app/Transformers/PiggyBankTransformer.php index ccc7e70b00..a418b93d37 100644 --- a/app/Transformers/PiggyBankTransformer.php +++ b/app/Transformers/PiggyBankTransformer.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Transformers; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\PiggyBank; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Support\Facades\Amount; diff --git a/app/Transformers/RecurrenceTransformer.php b/app/Transformers/RecurrenceTransformer.php index 00c388fdcb..823c4cecda 100644 --- a/app/Transformers/RecurrenceTransformer.php +++ b/app/Transformers/RecurrenceTransformer.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Transformers; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Recurrence; use Illuminate\Support\Facades\Log; diff --git a/app/Transformers/UserTransformer.php b/app/Transformers/UserTransformer.php index cff73173c2..0dff6bd273 100644 --- a/app/Transformers/UserTransformer.php +++ b/app/Transformers/UserTransformer.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Transformers; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; diff --git a/app/Transformers/WebhookMessageTransformer.php b/app/Transformers/WebhookMessageTransformer.php index 24bc25d4c2..8057d53a54 100644 --- a/app/Transformers/WebhookMessageTransformer.php +++ b/app/Transformers/WebhookMessageTransformer.php @@ -27,7 +27,6 @@ namespace FireflyIII\Transformers; use FireflyIII\Models\WebhookMessage; use Illuminate\Support\Facades\Log; use JsonException; - use function Safe\json_encode; /** diff --git a/app/User.php b/app/User.php index cc9e4b7d07..445ef22f77 100644 --- a/app/User.php +++ b/app/User.php @@ -25,6 +25,7 @@ declare(strict_types=1); namespace FireflyIII; use Deprecated; +use Exception; use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Events\RequestedNewPassword; use FireflyIII\Exceptions\FireflyException; @@ -67,7 +68,6 @@ use Illuminate\Support\Str; use Laravel\Passport\HasApiTokens; use NotificationChannels\Pushover\PushoverReceiver; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Exception; class User extends Authenticatable { diff --git a/app/Validation/Api/Data/Bulk/ValidatesBulkTransactionQuery.php b/app/Validation/Api/Data/Bulk/ValidatesBulkTransactionQuery.php index a218f323ee..2ec2ee2fc9 100644 --- a/app/Validation/Api/Data/Bulk/ValidatesBulkTransactionQuery.php +++ b/app/Validation/Api/Data/Bulk/ValidatesBulkTransactionQuery.php @@ -24,9 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Validation\Api\Data\Bulk; -use Illuminate\Validation\Validator; use FireflyIII\Repositories\Account\AccountRepositoryInterface; - +use Illuminate\Validation\Validator; use function Safe\json_decode; trait ValidatesBulkTransactionQuery diff --git a/app/Validation/CurrencyValidation.php b/app/Validation/CurrencyValidation.php index 2bdc1357d1..52bad8ff61 100644 --- a/app/Validation/CurrencyValidation.php +++ b/app/Validation/CurrencyValidation.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Validation; -use Illuminate\Validation\Validator; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Trait CurrencyValidation diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index e3d25b33f8..e13c866efc 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Validation; +use Config; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; @@ -45,15 +46,13 @@ use PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException; use PragmaRX\Google2FA\Exceptions\InvalidCharactersException; use PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException; use PragmaRX\Google2FALaravel\Facade; -use Config; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Safe\Exceptions\JsonException; use ValueError; - -use function Safe\preg_match; use function Safe\iconv; use function Safe\json_encode; +use function Safe\preg_match; /** * Class FireflyValidator. diff --git a/app/Validation/GroupValidation.php b/app/Validation/GroupValidation.php index bf4801856f..84c32cfc90 100644 --- a/app/Validation/GroupValidation.php +++ b/app/Validation/GroupValidation.php @@ -24,10 +24,10 @@ declare(strict_types=1); namespace FireflyIII\Validation; -use Illuminate\Validation\Validator; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionGroup; +use Illuminate\Validation\Validator; /** * Trait GroupValidation. diff --git a/app/Validation/RecurrenceValidation.php b/app/Validation/RecurrenceValidation.php index 2ba6e58a95..52b829203e 100644 --- a/app/Validation/RecurrenceValidation.php +++ b/app/Validation/RecurrenceValidation.php @@ -24,10 +24,10 @@ declare(strict_types=1); namespace FireflyIII\Validation; -use Illuminate\Validation\Validator; use Carbon\Carbon; use FireflyIII\Models\Recurrence; use FireflyIII\Models\RecurrenceTransaction; +use Illuminate\Validation\Validator; use InvalidArgumentException; /** diff --git a/app/Validation/TransactionValidation.php b/app/Validation/TransactionValidation.php index 944b9372d2..b44333bd90 100644 --- a/app/Validation/TransactionValidation.php +++ b/app/Validation/TransactionValidation.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Validation; -use Illuminate\Validation\Validator; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Exceptions\FireflyException; @@ -37,6 +36,7 @@ use FireflyIII\Repositories\Account\AccountRepository; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\User; use Illuminate\Support\Facades\Log; +use Illuminate\Validation\Validator; /** * Trait TransactionValidation From b0033cf9ed4386c55facc4d396060f4b778cda29 Mon Sep 17 00:00:00 2001 From: JC5 Date: Sun, 5 Oct 2025 13:03:51 +0200 Subject: [PATCH 82/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-10-05?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../V1/Controllers/Chart/BudgetController.php | 1 - .../Data/Export/ExportController.php | 36 +-- .../Models/Account/ListController.php | 5 - .../Models/Account/ShowController.php | 1 - .../Models/Attachment/ShowController.php | 1 - .../Models/AvailableBudget/ShowController.php | 1 - .../Models/Bill/ListController.php | 3 - .../Models/Bill/ShowController.php | 1 - .../Models/Budget/ListController.php | 4 - .../Models/Budget/ShowController.php | 1 - .../Models/BudgetLimit/ListController.php | 1 - .../Models/Category/ListController.php | 2 - .../Models/Category/ShowController.php | 1 - .../Models/ObjectGroup/ListController.php | 2 - .../Models/PiggyBank/ListController.php | 3 - .../Models/PiggyBank/ShowController.php | 1 - .../Models/Recurrence/ListController.php | 1 - .../Models/Recurrence/ShowController.php | 1 - .../Models/Recurrence/TriggerController.php | 2 +- .../Models/Rule/ShowController.php | 1 - .../Models/RuleGroup/ListController.php | 1 - .../Models/RuleGroup/ShowController.php | 1 - .../Controllers/Models/Tag/ListController.php | 2 - .../Controllers/Models/Tag/ShowController.php | 1 - .../Models/Transaction/ListController.php | 3 - .../Models/Transaction/ShowController.php | 1 - .../TransactionCurrency/ListController.php | 7 - .../TransactionCurrency/ShowController.php | 3 - .../TransactionCurrency/StoreController.php | 1 - .../TransactionCurrency/UpdateController.php | 3 - .../Models/TransactionLink/ShowController.php | 1 - .../TransactionLinkType/ListController.php | 1 - .../TransactionLinkType/ShowController.php | 1 - .../Search/TransactionController.php | 1 - .../V1/Controllers/System/UserController.php | 1 - .../User/PreferencesController.php | 1 - .../Controllers/Webhook/MessageController.php | 1 - .../V1/Controllers/Webhook/ShowController.php | 1 - .../Requests/Data/Bulk/TransactionRequest.php | 1 + app/Console/Commands/Export/ExportsData.php | 4 +- .../Commands/System/ForcesDecimalSize.php | 1 + .../Commands/System/ResetsErrorMailLimit.php | 1 + .../Commands/System/ScansAttachments.php | 1 + .../Commands/System/VerifySecurityAlerts.php | 2 +- .../Upgrade/AddsTransactionIdentifiers.php | 2 +- .../Upgrade/RemovesDatabaseDecryption.php | 2 + .../Upgrade/UpgradesAccountCurrencies.php | 2 - .../Upgrade/UpgradesAccountMetaData.php | 1 + .../Commands/Upgrade/UpgradesAttachments.php | 1 + .../Commands/Upgrade/UpgradesBillsToRules.php | 2 +- .../Commands/Upgrade/UpgradesBudgetLimits.php | 1 + .../Upgrade/UpgradesCurrencyPreferences.php | 1 + .../Commands/Upgrade/UpgradesDatabase.php | 1 + .../Commands/Upgrade/UpgradesJournalNotes.php | 1 + .../Commands/Upgrade/UpgradesLiabilities.php | 1 + .../Upgrade/UpgradesLiabilitiesEight.php | 1 + .../Upgrade/UpgradesMultiPiggyBanks.php | 1 + .../UpgradesPrimaryCurrencyAmounts.php | 1 + .../Upgrade/UpgradesRecurrenceMetaData.php | 2 + .../Commands/Upgrade/UpgradesRuleActions.php | 1 + .../Commands/Upgrade/UpgradesTagLocations.php | 1 + .../Commands/Upgrade/UpgradesToGroups.php | 1 + .../Upgrade/UpgradesTransferCurrencies.php | 1 + .../UpgradesVariousCurrencyInformation.php | 1 + .../Commands/Upgrade/UpgradesWebhooks.php | 1 + app/Console/Commands/VerifiesAccessToken.php | 1 - app/Exceptions/Handler.php | 1 + app/Factory/AccountFactory.php | 3 +- app/Factory/PiggyBankFactory.php | 1 + app/Factory/TransactionJournalFactory.php | 5 +- app/Handlers/Events/BillEventHandler.php | 1 + app/Handlers/Events/UserEventHandler.php | 5 - .../Events/VersionCheckEventHandler.php | 4 - app/Helpers/Attachments/AttachmentHelper.php | 6 +- .../Collector/Extensions/MetaCollection.php | 1 + app/Helpers/Collector/GroupCollector.php | 4 +- app/Helpers/Fiscal/FiscalHelper.php | 3 +- .../Webhook/Sha3SignatureGenerator.php | 1 + .../Controllers/Account/CreateController.php | 2 - .../Controllers/Account/IndexController.php | 6 - .../Controllers/Account/ShowController.php | 8 - app/Http/Controllers/Admin/HomeController.php | 1 + app/Http/Controllers/Admin/UserController.php | 1 + .../Auth/ForgotPasswordController.php | 5 +- app/Http/Controllers/Auth/LoginController.php | 2 - .../Controllers/Auth/RegisterController.php | 5 - .../Auth/ResetPasswordController.php | 3 +- .../Controllers/Auth/TwoFactorController.php | 3 +- app/Http/Controllers/Bill/IndexController.php | 2 - app/Http/Controllers/Bill/ShowController.php | 4 +- .../Controllers/Budget/ShowController.php | 15 +- .../Controllers/Category/IndexController.php | 3 +- .../Category/NoCategoryController.php | 7 +- .../Controllers/Category/ShowController.php | 9 +- .../Controllers/Chart/AccountController.php | 6 +- .../Controllers/Chart/CategoryController.php | 5 - .../Controllers/Chart/PiggyBankController.php | 1 - app/Http/Controllers/Controller.php | 1 + app/Http/Controllers/DebugController.php | 2 + app/Http/Controllers/JavascriptController.php | 4 - app/Http/Controllers/Json/IntroController.php | 2 - app/Http/Controllers/NewUserController.php | 1 - .../Controllers/PiggyBank/IndexController.php | 1 - .../Controllers/PreferencesController.php | 3 +- .../Controllers/Profile/MfaController.php | 6 - app/Http/Controllers/ProfileController.php | 1 - .../Controllers/Recurring/IndexController.php | 2 - app/Http/Controllers/ReportController.php | 3 +- .../Controllers/System/InstallController.php | 1 + app/Http/Controllers/TagController.php | 9 +- .../Transaction/CreateController.php | 3 +- .../Transaction/EditController.php | 4 +- .../Transaction/IndexController.php | 9 +- .../TransactionCurrency/DeleteController.php | 8 +- .../TransactionCurrency/EditController.php | 4 +- .../TransactionCurrency/IndexController.php | 3 +- app/Http/Middleware/AcceptHeaders.php | 4 +- app/Http/Requests/ReportFormRequest.php | 3 +- app/Jobs/DownloadExchangeRates.php | 3 +- app/Jobs/MailError.php | 1 + app/Mail/InvitationMail.php | 1 + app/Mail/ReportNewJournalsMail.php | 1 + app/Models/AccountMeta.php | 1 + app/Models/BudgetLimit.php | 13 +- app/Models/Configuration.php | 1 + app/Models/Recurrence.php | 4 +- app/Models/TransactionGroup.php | 5 +- app/Models/TransactionJournal.php | 1 + app/Models/TransactionJournalMeta.php | 1 + app/Providers/AppServiceProvider.php | 1 + .../Account/AccountRepository.php | 1 + app/Repositories/Account/AccountTasker.php | 4 - .../Budget/AvailableBudgetRepository.php | 2 +- app/Repositories/Budget/BudgetRepository.php | 4 +- .../Budget/NoBudgetRepository.php | 3 +- .../Category/CategoryRepository.php | 1 + .../Currency/CurrencyRepository.php | 5 +- .../Journal/JournalCLIRepository.php | 1 + .../Recurring/RecurringRepository.php | 2 +- .../TransactionGroupRepository.php | 2 + app/Repositories/User/UserRepository.php | 2 +- .../UserGroup/UserGroupRepository.php | 2 - app/Rules/IsValidBulkClause.php | 1 + app/Rules/IsValidPositiveAmount.php | 1 + app/Rules/UniqueAccountNumber.php | 1 + .../FireflyIIIOrg/Update/UpdateRequest.php | 1 + .../Internal/Support/AccountServiceTrait.php | 2 - .../Internal/Support/JournalServiceTrait.php | 6 +- .../Support/RecurringTransactionTrait.php | 1 + .../Internal/Update/AccountUpdateService.php | 2 - .../Internal/Update/BillUpdateService.php | 2 - .../Webhook/StandardWebhookSender.php | 1 + app/Support/Binder/CLIToken.php | 1 - app/Support/CacheProperties.php | 1 + app/Support/Export/ExportDataGenerator.php | 2 - app/Support/Http/Controllers/CreateStuff.php | 1 + .../Http/Controllers/RequestInformation.php | 1 + .../Enrichments/BudgetLimitEnrichment.php | 12 +- .../Enrichments/PiggyBankEnrichment.php | 2 +- .../Enrichments/RecurringEnrichment.php | 3 +- app/Support/Navigation.php | 26 +- app/Support/ParseDateString.php | 5 +- app/Support/Preferences.php | 2 - .../Report/Budget/BudgetReportGenerator.php | 2 - app/Support/Request/ConvertsDataTypes.php | 1 + app/Support/Search/AccountSearch.php | 1 + app/Support/Search/OperatorQuerySearch.php | 2 - .../Search/QueryParser/GdbotsQueryParser.php | 4 +- app/Support/Steam.php | 4 +- app/Support/System/OAuthKeys.php | 2 +- app/Support/Twig/General.php | 1 + app/Support/Twig/TransactionGroupTwig.php | 1 + app/Transformers/PiggyBankTransformer.php | 1 - app/Transformers/RecurrenceTransformer.php | 1 - app/Transformers/UserTransformer.php | 1 - .../WebhookMessageTransformer.php | 1 + .../Bulk/ValidatesBulkTransactionQuery.php | 1 + app/Validation/FireflyValidator.php | 5 +- config/firefly.php | 4 +- package-lock.json | 238 +++++++++--------- 180 files changed, 285 insertions(+), 455 deletions(-) diff --git a/app/Api/V1/Controllers/Chart/BudgetController.php b/app/Api/V1/Controllers/Chart/BudgetController.php index 194bcf0362..6f0425d6fb 100644 --- a/app/Api/V1/Controllers/Chart/BudgetController.php +++ b/app/Api/V1/Controllers/Chart/BudgetController.php @@ -259,7 +259,6 @@ class BudgetController extends Controller return $return; } - private function filterLimit(int $currencyId, Collection $limits): ?BudgetLimit { $amount = '0'; diff --git a/app/Api/V1/Controllers/Data/Export/ExportController.php b/app/Api/V1/Controllers/Data/Export/ExportController.php index 685217f769..7e78ca920e 100644 --- a/app/Api/V1/Controllers/Data/Export/ExportController.php +++ b/app/Api/V1/Controllers/Data/Export/ExportController.php @@ -31,6 +31,7 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Support\Export\ExportDataGenerator; use Illuminate\Http\Response as LaravelResponse; use Safe\Exceptions\DatetimeException; + use function Safe\date; /** @@ -60,11 +61,9 @@ class ExportController extends Controller } /** - * @param ExportRequest $request - * - * @return LaravelResponse * @throws DatetimeException * @throws FireflyException + * * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ public function accounts(ExportRequest $request): LaravelResponse @@ -102,11 +101,9 @@ class ExportController extends Controller } /** - * @param ExportRequest $request - * - * @return LaravelResponse * @throws DatetimeException * @throws FireflyException + * * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ public function bills(ExportRequest $request): LaravelResponse @@ -117,11 +114,9 @@ class ExportController extends Controller } /** - * @param ExportRequest $request - * - * @return LaravelResponse * @throws DatetimeException * @throws FireflyException + * * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ public function budgets(ExportRequest $request): LaravelResponse @@ -132,11 +127,9 @@ class ExportController extends Controller } /** - * @param ExportRequest $request - * - * @return LaravelResponse * @throws DatetimeException * @throws FireflyException + * * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ public function categories(ExportRequest $request): LaravelResponse @@ -147,11 +140,9 @@ class ExportController extends Controller } /** - * @param ExportRequest $request - * - * @return LaravelResponse * @throws DatetimeException * @throws FireflyException + * * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ public function piggyBanks(ExportRequest $request): LaravelResponse @@ -162,11 +153,9 @@ class ExportController extends Controller } /** - * @param ExportRequest $request - * - * @return LaravelResponse * @throws DatetimeException * @throws FireflyException + * * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ public function recurring(ExportRequest $request): LaravelResponse @@ -177,11 +166,9 @@ class ExportController extends Controller } /** - * @param ExportRequest $request - * - * @return LaravelResponse * @throws DatetimeException * @throws FireflyException + * * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ public function rules(ExportRequest $request): LaravelResponse @@ -192,11 +179,9 @@ class ExportController extends Controller } /** - * @param ExportRequest $request - * - * @return LaravelResponse * @throws DatetimeException * @throws FireflyException + * * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ public function tags(ExportRequest $request): LaravelResponse @@ -207,9 +192,6 @@ class ExportController extends Controller } /** - * @param ExportRequest $request - * - * @return LaravelResponse * @throws DatetimeException * @throws FireflyException */ diff --git a/app/Api/V1/Controllers/Models/Account/ListController.php b/app/Api/V1/Controllers/Models/Account/ListController.php index 6ea2908f76..b230572b93 100644 --- a/app/Api/V1/Controllers/Models/Account/ListController.php +++ b/app/Api/V1/Controllers/Models/Account/ListController.php @@ -69,8 +69,6 @@ class ListController extends Controller ); } - /** - */ public function attachments(Account $account): JsonResponse { $manager = $this->getManager(); @@ -94,8 +92,6 @@ class ListController extends Controller return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } - /** - */ public function piggyBanks(Account $account): JsonResponse { // create some objects: @@ -132,7 +128,6 @@ class ListController extends Controller /** * Show all transaction groups related to the account. - * */ public function transactions(Request $request, Account $account): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Account/ShowController.php b/app/Api/V1/Controllers/Models/Account/ShowController.php index 74c5fba684..230246bcf1 100644 --- a/app/Api/V1/Controllers/Models/Account/ShowController.php +++ b/app/Api/V1/Controllers/Models/Account/ShowController.php @@ -67,7 +67,6 @@ class ShowController extends Controller /** * Display a listing of the resource. - * */ public function index(ShowRequest $request): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Attachment/ShowController.php b/app/Api/V1/Controllers/Models/Attachment/ShowController.php index 3785d30b35..273c8dc986 100644 --- a/app/Api/V1/Controllers/Models/Attachment/ShowController.php +++ b/app/Api/V1/Controllers/Models/Attachment/ShowController.php @@ -119,7 +119,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/attachments/listAttachment * * Display a listing of the resource. - * */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php b/app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php index 9ee8b50278..ee0c01610f 100644 --- a/app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php +++ b/app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php @@ -66,7 +66,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/available_budgets/getAvailableBudget * * Display a listing of the resource. - * */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Bill/ListController.php b/app/Api/V1/Controllers/Models/Bill/ListController.php index e6a98f6716..e33c6f5804 100644 --- a/app/Api/V1/Controllers/Models/Bill/ListController.php +++ b/app/Api/V1/Controllers/Models/Bill/ListController.php @@ -70,7 +70,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/bills/listAttachmentByBill * * Display a listing of the resource. - * */ public function attachments(Bill $bill): JsonResponse { @@ -100,7 +99,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/bills/listRuleByBill * * List all of them. - * */ public function rules(Bill $bill): JsonResponse { @@ -132,7 +130,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/bills/listTransactionByBill * * Show all transactions. - * */ public function transactions(Request $request, Bill $bill): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Bill/ShowController.php b/app/Api/V1/Controllers/Models/Bill/ShowController.php index 7ca52a322e..396c6e4db8 100644 --- a/app/Api/V1/Controllers/Models/Bill/ShowController.php +++ b/app/Api/V1/Controllers/Models/Bill/ShowController.php @@ -64,7 +64,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/bills/listBill * * Display a listing of the resource. - * */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Budget/ListController.php b/app/Api/V1/Controllers/Models/Budget/ListController.php index 17b3f95a94..f2a80bbcb7 100644 --- a/app/Api/V1/Controllers/Models/Budget/ListController.php +++ b/app/Api/V1/Controllers/Models/Budget/ListController.php @@ -72,7 +72,6 @@ class ListController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/budgets/listAttachmentByBudget - * */ public function attachments(Budget $budget): JsonResponse { @@ -102,7 +101,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/budgets/listBudgetLimitByBudget * * Display a listing of the resource. - * */ public function budgetLimits(Budget $budget): JsonResponse { @@ -137,7 +135,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/budgets/listTransactionByBudget * * Show all transactions. - * */ public function transactions(Request $request, Budget $budget): JsonResponse { @@ -198,7 +195,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/budgets/listTransactionWithoutBudget * * Show all transactions. - * */ public function withoutBudget(Request $request): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Budget/ShowController.php b/app/Api/V1/Controllers/Models/Budget/ShowController.php index 55994b1fc0..7b3dfde927 100644 --- a/app/Api/V1/Controllers/Models/Budget/ShowController.php +++ b/app/Api/V1/Controllers/Models/Budget/ShowController.php @@ -68,7 +68,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/budgets/listBudget * * Display a listing of the resource. - * */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php b/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php index 0e8d707900..f21f276843 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php @@ -48,7 +48,6 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/budgets/listTransactionByBudgetLimit * Show all transactions. - * */ public function transactions(Request $request, Budget $budget, BudgetLimit $budgetLimit): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Category/ListController.php b/app/Api/V1/Controllers/Models/Category/ListController.php index fbadd0c42a..fa71b0a631 100644 --- a/app/Api/V1/Controllers/Models/Category/ListController.php +++ b/app/Api/V1/Controllers/Models/Category/ListController.php @@ -67,7 +67,6 @@ class ListController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/categories/listAttachmentByCategory - * */ public function attachments(Category $category): JsonResponse { @@ -97,7 +96,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/categories/listTransactionByCategory * * Show all transactions. - * */ public function transactions(Request $request, Category $category): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Category/ShowController.php b/app/Api/V1/Controllers/Models/Category/ShowController.php index 0f827ed175..2fc4137ad7 100644 --- a/app/Api/V1/Controllers/Models/Category/ShowController.php +++ b/app/Api/V1/Controllers/Models/Category/ShowController.php @@ -64,7 +64,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/categories/listCategory * * Display a listing of the resource. - * */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/ObjectGroup/ListController.php b/app/Api/V1/Controllers/Models/ObjectGroup/ListController.php index cac0aa7cc5..86a5d8bac4 100644 --- a/app/Api/V1/Controllers/Models/ObjectGroup/ListController.php +++ b/app/Api/V1/Controllers/Models/ObjectGroup/ListController.php @@ -67,7 +67,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/object_groups/listBillByObjectGroup * * List all bills in this object group - * */ public function bills(ObjectGroup $objectGroup): JsonResponse { @@ -107,7 +106,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/object_groups/listPiggyBankByObjectGroup * * List all piggies under the object group. - * */ public function piggyBanks(ObjectGroup $objectGroup): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/PiggyBank/ListController.php b/app/Api/V1/Controllers/Models/PiggyBank/ListController.php index 2eff6ea53c..e22e88337d 100644 --- a/app/Api/V1/Controllers/Models/PiggyBank/ListController.php +++ b/app/Api/V1/Controllers/Models/PiggyBank/ListController.php @@ -66,7 +66,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/piggy_banks/listAccountByPiggyBank * * List single resource. - * */ public function accounts(PiggyBank $piggyBank): JsonResponse { @@ -103,7 +102,6 @@ class ListController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/piggy_banks/listAttachmentByPiggyBank - * */ public function attachments(PiggyBank $piggyBank): JsonResponse { @@ -133,7 +131,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/piggy_banks/listEventByPiggyBank * * List single resource. - * */ public function piggyBankEvents(PiggyBank $piggyBank): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php b/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php index 5f6274e14e..7fb1fb5e71 100644 --- a/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php +++ b/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php @@ -64,7 +64,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/piggy_banks/listPiggyBank * * List all of them. - * */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Recurrence/ListController.php b/app/Api/V1/Controllers/Models/Recurrence/ListController.php index ea5ed5786d..ce6a9d61a9 100644 --- a/app/Api/V1/Controllers/Models/Recurrence/ListController.php +++ b/app/Api/V1/Controllers/Models/Recurrence/ListController.php @@ -67,7 +67,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/recurrences/listTransactionByRecurrence * * Show transactions for this recurrence. - * */ public function transactions(Request $request, Recurrence $recurrence): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Recurrence/ShowController.php b/app/Api/V1/Controllers/Models/Recurrence/ShowController.php index d08117d869..7b3b9a23b9 100644 --- a/app/Api/V1/Controllers/Models/Recurrence/ShowController.php +++ b/app/Api/V1/Controllers/Models/Recurrence/ShowController.php @@ -64,7 +64,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/recurrences/listRecurrence * * List all of them. - * */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Recurrence/TriggerController.php b/app/Api/V1/Controllers/Models/Recurrence/TriggerController.php index e9662efaf1..7f4ad30b60 100644 --- a/app/Api/V1/Controllers/Models/Recurrence/TriggerController.php +++ b/app/Api/V1/Controllers/Models/Recurrence/TriggerController.php @@ -87,7 +87,7 @@ class TriggerController extends Controller // enrich groups and return them: - $paginator = new LengthAwarePaginator(new Collection(), 0, 1); + $paginator = new LengthAwarePaginator(new Collection(), 0, 1); if ($groups->count() > 0) { /** @var User $admin */ $admin = auth()->user(); diff --git a/app/Api/V1/Controllers/Models/Rule/ShowController.php b/app/Api/V1/Controllers/Models/Rule/ShowController.php index d18599802c..045c61ad28 100644 --- a/app/Api/V1/Controllers/Models/Rule/ShowController.php +++ b/app/Api/V1/Controllers/Models/Rule/ShowController.php @@ -66,7 +66,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/rules/listRule * * List all of them. - * */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/RuleGroup/ListController.php b/app/Api/V1/Controllers/Models/RuleGroup/ListController.php index d39590f831..2d296ce903 100644 --- a/app/Api/V1/Controllers/Models/RuleGroup/ListController.php +++ b/app/Api/V1/Controllers/Models/RuleGroup/ListController.php @@ -63,7 +63,6 @@ class ListController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/rule_groups/listRuleByGroup - * */ public function rules(RuleGroup $group): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/RuleGroup/ShowController.php b/app/Api/V1/Controllers/Models/RuleGroup/ShowController.php index cd9d42c2d3..b5f08d9d9a 100644 --- a/app/Api/V1/Controllers/Models/RuleGroup/ShowController.php +++ b/app/Api/V1/Controllers/Models/RuleGroup/ShowController.php @@ -65,7 +65,6 @@ class ShowController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/rule_groups/listRuleGroup * List all of them. - * */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Tag/ListController.php b/app/Api/V1/Controllers/Models/Tag/ListController.php index 49899a4eec..aed5652f85 100644 --- a/app/Api/V1/Controllers/Models/Tag/ListController.php +++ b/app/Api/V1/Controllers/Models/Tag/ListController.php @@ -70,7 +70,6 @@ class ListController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/tags/listAttachmentByTag - * */ public function attachments(Tag $tag): JsonResponse { @@ -100,7 +99,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/tags/listTransactionByTag * * Show all transactions. - * */ public function transactions(Request $request, Tag $tag): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Tag/ShowController.php b/app/Api/V1/Controllers/Models/Tag/ShowController.php index f69d050ddc..f239cb35c5 100644 --- a/app/Api/V1/Controllers/Models/Tag/ShowController.php +++ b/app/Api/V1/Controllers/Models/Tag/ShowController.php @@ -66,7 +66,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/tags/listTag * * List all of them. - * */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Transaction/ListController.php b/app/Api/V1/Controllers/Models/Transaction/ListController.php index fbc68db891..ab96a00af7 100644 --- a/app/Api/V1/Controllers/Models/Transaction/ListController.php +++ b/app/Api/V1/Controllers/Models/Transaction/ListController.php @@ -68,7 +68,6 @@ class ListController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/listAttachmentByTransaction - * */ public function attachments(TransactionGroup $transactionGroup): JsonResponse { @@ -99,7 +98,6 @@ class ListController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/listEventByTransaction - * */ public function piggyBankEvents(TransactionGroup $transactionGroup): JsonResponse { @@ -141,7 +139,6 @@ class ListController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/listLinksByJournal - * */ public function transactionLinks(TransactionJournal $transactionJournal): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Transaction/ShowController.php b/app/Api/V1/Controllers/Models/Transaction/ShowController.php index 90942fff14..ee45ded96d 100644 --- a/app/Api/V1/Controllers/Models/Transaction/ShowController.php +++ b/app/Api/V1/Controllers/Models/Transaction/ShowController.php @@ -51,7 +51,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/listTransaction * * Show all transactions. - * */ public function index(Request $request): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php index 02bec34c6a..53d9beea16 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php @@ -70,7 +70,6 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/listAccountByCurrency * Display a list of accounts. - * */ public function accounts(Request $request, TransactionCurrency $currency): JsonResponse { @@ -127,7 +126,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/listAvailableBudgetByCurrency * * Display a listing of the resource. - * */ public function availableBudgets(TransactionCurrency $currency): JsonResponse { @@ -161,7 +159,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/listBillByCurrency * * List all bills - * */ public function bills(TransactionCurrency $currency): JsonResponse { @@ -207,7 +204,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/listBudgetLimitByCurrency * * List all budget limits - * */ public function budgetLimits(TransactionCurrency $currency): JsonResponse { @@ -244,7 +240,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/listRecurrenceByCurrency * * List all recurring transactions. - * */ public function recurrences(TransactionCurrency $currency): JsonResponse { @@ -296,7 +291,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/listRuleByCurrency * * List all of them. - * */ public function rules(TransactionCurrency $currency): JsonResponse { @@ -340,7 +334,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/listTransactionByCurrency * * Show all transactions. - * */ public function transactions(Request $request, TransactionCurrency $currency): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php index 8f4168ae1e..d0920b00f5 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php @@ -68,7 +68,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/listCurrency * * Display a listing of the resource. - * */ public function index(): JsonResponse { @@ -97,7 +96,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/getCurrency * * Show a currency. - * */ public function show(TransactionCurrency $currency): JsonResponse { @@ -120,7 +118,6 @@ class ShowController extends Controller /** * Show a currency. - * */ public function showPrimary(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php index bbead26e16..56b61b9c99 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php @@ -65,7 +65,6 @@ class StoreController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/storeCurrency * * Store new currency. - * */ public function store(StoreRequest $request): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php index eca115a7d8..f76bfaf8a2 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php @@ -98,8 +98,6 @@ class UpdateController extends Controller return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } - /** - */ public function makePrimary(TransactionCurrency $currency): JsonResponse { /** @var User $user */ @@ -126,7 +124,6 @@ class UpdateController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/enableCurrency * * Enable a currency. - * */ public function enable(TransactionCurrency $currency): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/TransactionLink/ShowController.php b/app/Api/V1/Controllers/Models/TransactionLink/ShowController.php index a45be78c09..fb2e80204d 100644 --- a/app/Api/V1/Controllers/Models/TransactionLink/ShowController.php +++ b/app/Api/V1/Controllers/Models/TransactionLink/ShowController.php @@ -68,7 +68,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/links/listTransactionLink * * List all transaction links there are. - * */ public function index(Request $request): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php b/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php index a71cfa46e9..03537cfe62 100644 --- a/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php +++ b/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php @@ -67,7 +67,6 @@ class ListController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/links/listTransactionByLinkType - * */ public function transactions(Request $request, LinkType $linkType): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/TransactionLinkType/ShowController.php b/app/Api/V1/Controllers/Models/TransactionLinkType/ShowController.php index 65af2aa929..27c7a4eb15 100644 --- a/app/Api/V1/Controllers/Models/TransactionLinkType/ShowController.php +++ b/app/Api/V1/Controllers/Models/TransactionLinkType/ShowController.php @@ -66,7 +66,6 @@ class ShowController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/links/listLinkType - * */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Search/TransactionController.php b/app/Api/V1/Controllers/Search/TransactionController.php index 040e672934..d95daf8c37 100644 --- a/app/Api/V1/Controllers/Search/TransactionController.php +++ b/app/Api/V1/Controllers/Search/TransactionController.php @@ -41,7 +41,6 @@ class TransactionController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/search/searchTransactions - * */ public function search(Request $request, SearchInterface $searcher): JsonResponse { diff --git a/app/Api/V1/Controllers/System/UserController.php b/app/Api/V1/Controllers/System/UserController.php index edffb84c44..d08807ab52 100644 --- a/app/Api/V1/Controllers/System/UserController.php +++ b/app/Api/V1/Controllers/System/UserController.php @@ -90,7 +90,6 @@ class UserController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/users/listUser * * Display a listing of the resource. - * */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/User/PreferencesController.php b/app/Api/V1/Controllers/User/PreferencesController.php index 552d10c8e0..e5a97f7628 100644 --- a/app/Api/V1/Controllers/User/PreferencesController.php +++ b/app/Api/V1/Controllers/User/PreferencesController.php @@ -49,7 +49,6 @@ class PreferencesController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/preferences/listPreference * * List all of them. - * */ public function index(): JsonResponse { diff --git a/app/Api/V1/Controllers/Webhook/MessageController.php b/app/Api/V1/Controllers/Webhook/MessageController.php index a5426ca91d..475724f4c5 100644 --- a/app/Api/V1/Controllers/Webhook/MessageController.php +++ b/app/Api/V1/Controllers/Webhook/MessageController.php @@ -62,7 +62,6 @@ class MessageController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/webhooks/getWebhookMessages - * */ public function index(Webhook $webhook): JsonResponse { diff --git a/app/Api/V1/Controllers/Webhook/ShowController.php b/app/Api/V1/Controllers/Webhook/ShowController.php index 466f0e8a90..62c39258b4 100644 --- a/app/Api/V1/Controllers/Webhook/ShowController.php +++ b/app/Api/V1/Controllers/Webhook/ShowController.php @@ -69,7 +69,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/webhooks/listWebhook * * Display a listing of the webhooks of the user. - * */ public function index(): JsonResponse { diff --git a/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php b/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php index 14b660d7fe..902967a9c2 100644 --- a/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php +++ b/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php @@ -33,6 +33,7 @@ use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; use JsonException; + use function Safe\json_decode; /** diff --git a/app/Console/Commands/Export/ExportsData.php b/app/Console/Commands/Export/ExportsData.php index cd1d53e9f5..90a8c9602b 100644 --- a/app/Console/Commands/Export/ExportsData.php +++ b/app/Console/Commands/Export/ExportsData.php @@ -40,6 +40,7 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use InvalidArgumentException; use Safe\Exceptions\FilesystemException; + use function Safe\file_put_contents; class ExportsData extends Command @@ -275,9 +276,6 @@ class ExportsData extends Command } /** - * @param array $options - * @param array $data - * * @throws FireflyException * @throws FilesystemException */ diff --git a/app/Console/Commands/System/ForcesDecimalSize.php b/app/Console/Commands/System/ForcesDecimalSize.php index c92183b8c3..4cbbe5546c 100644 --- a/app/Console/Commands/System/ForcesDecimalSize.php +++ b/app/Console/Commands/System/ForcesDecimalSize.php @@ -42,6 +42,7 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; + use function Safe\json_encode; use function Safe\mb_regex_encoding; diff --git a/app/Console/Commands/System/ResetsErrorMailLimit.php b/app/Console/Commands/System/ResetsErrorMailLimit.php index c896842a17..ffba1aad82 100644 --- a/app/Console/Commands/System/ResetsErrorMailLimit.php +++ b/app/Console/Commands/System/ResetsErrorMailLimit.php @@ -28,6 +28,7 @@ namespace FireflyIII\Console\Commands\System; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use Illuminate\Console\Command; use Symfony\Component\Console\Command\Command as CommandAlias; + use function Safe\file_put_contents; use function Safe\json_encode; diff --git a/app/Console/Commands/System/ScansAttachments.php b/app/Console/Commands/System/ScansAttachments.php index 0e9b8c8128..0225cf0008 100644 --- a/app/Console/Commands/System/ScansAttachments.php +++ b/app/Console/Commands/System/ScansAttachments.php @@ -34,6 +34,7 @@ use Illuminate\Support\Facades\Storage; use Safe\Exceptions\FileinfoException; use Safe\Exceptions\FilesystemException; use Safe\Exceptions\StringsException; + use function Safe\file_put_contents; use function Safe\md5_file; use function Safe\mime_content_type; diff --git a/app/Console/Commands/System/VerifySecurityAlerts.php b/app/Console/Commands/System/VerifySecurityAlerts.php index 878f762711..ff801050b9 100644 --- a/app/Console/Commands/System/VerifySecurityAlerts.php +++ b/app/Console/Commands/System/VerifySecurityAlerts.php @@ -31,6 +31,7 @@ use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; use League\Flysystem\FilesystemException; use Safe\Exceptions\JsonException; + use function Safe\json_decode; class VerifySecurityAlerts extends Command @@ -44,7 +45,6 @@ class VerifySecurityAlerts extends Command /** * Execute the console command. * - * @return int * @throws FilesystemException * @throws JsonException */ diff --git a/app/Console/Commands/Upgrade/AddsTransactionIdentifiers.php b/app/Console/Commands/Upgrade/AddsTransactionIdentifiers.php index 79a8f0bfaf..baef09da3d 100644 --- a/app/Console/Commands/Upgrade/AddsTransactionIdentifiers.php +++ b/app/Console/Commands/Upgrade/AddsTransactionIdentifiers.php @@ -52,7 +52,6 @@ class AddsTransactionIdentifiers extends Command * * When either of these are the same amount, FF3 can't keep them apart: +3/-3, +3/-3, +3/-3. This happens more * often than you would think. So each set gets a number (1,2,3) to keep them apart. - * */ public function handle(): int { @@ -99,6 +98,7 @@ class AddsTransactionIdentifiers extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + return (bool)$configVar?->data; } diff --git a/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php b/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php index cf66e8cc06..03070692ef 100644 --- a/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php +++ b/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php @@ -34,6 +34,7 @@ use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; use JsonException; use stdClass; + use function Safe\json_decode; class RemovesDatabaseDecryption extends Command @@ -98,6 +99,7 @@ class RemovesDatabaseDecryption extends Command } catch (FireflyException $e) { Log::error($e->getMessage()); } + return (bool)$configVar?->data; } diff --git a/app/Console/Commands/Upgrade/UpgradesAccountCurrencies.php b/app/Console/Commands/Upgrade/UpgradesAccountCurrencies.php index 50a9fc71c1..cb1a35cc55 100644 --- a/app/Console/Commands/Upgrade/UpgradesAccountCurrencies.php +++ b/app/Console/Commands/Upgrade/UpgradesAccountCurrencies.php @@ -98,8 +98,6 @@ class UpgradesAccountCurrencies extends Command } } - /** - */ private function updateCurrenciesForUser(User $user): void { $this->accountRepos->setUser($user); diff --git a/app/Console/Commands/Upgrade/UpgradesAccountMetaData.php b/app/Console/Commands/Upgrade/UpgradesAccountMetaData.php index 7e8f6168cc..234fc3cba8 100644 --- a/app/Console/Commands/Upgrade/UpgradesAccountMetaData.php +++ b/app/Console/Commands/Upgrade/UpgradesAccountMetaData.php @@ -83,6 +83,7 @@ class UpgradesAccountMetaData extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + return (bool)$configVar?->data; } diff --git a/app/Console/Commands/Upgrade/UpgradesAttachments.php b/app/Console/Commands/Upgrade/UpgradesAttachments.php index b9cfea1ed6..5c8022345b 100644 --- a/app/Console/Commands/Upgrade/UpgradesAttachments.php +++ b/app/Console/Commands/Upgrade/UpgradesAttachments.php @@ -93,6 +93,7 @@ class UpgradesAttachments extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + return (bool)$configVar?->data; } diff --git a/app/Console/Commands/Upgrade/UpgradesBillsToRules.php b/app/Console/Commands/Upgrade/UpgradesBillsToRules.php index 62d43c1889..a244f74738 100644 --- a/app/Console/Commands/Upgrade/UpgradesBillsToRules.php +++ b/app/Console/Commands/Upgrade/UpgradesBillsToRules.php @@ -99,13 +99,13 @@ class UpgradesBillsToRules extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + return (bool)$configVar?->data; } /** * Migrate bills to new rule structure for a specific user. - * */ private function migrateUser(User $user): void { diff --git a/app/Console/Commands/Upgrade/UpgradesBudgetLimits.php b/app/Console/Commands/Upgrade/UpgradesBudgetLimits.php index 6430407ea4..2b55dabfd5 100644 --- a/app/Console/Commands/Upgrade/UpgradesBudgetLimits.php +++ b/app/Console/Commands/Upgrade/UpgradesBudgetLimits.php @@ -85,6 +85,7 @@ class UpgradesBudgetLimits extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + return (bool)$configVar?->data; } diff --git a/app/Console/Commands/Upgrade/UpgradesCurrencyPreferences.php b/app/Console/Commands/Upgrade/UpgradesCurrencyPreferences.php index ddecd04fef..92b8b0445b 100644 --- a/app/Console/Commands/Upgrade/UpgradesCurrencyPreferences.php +++ b/app/Console/Commands/Upgrade/UpgradesCurrencyPreferences.php @@ -66,6 +66,7 @@ class UpgradesCurrencyPreferences extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + return (bool)$configVar?->data; } diff --git a/app/Console/Commands/Upgrade/UpgradesDatabase.php b/app/Console/Commands/Upgrade/UpgradesDatabase.php index bbc6c8ee68..32ff857c9e 100644 --- a/app/Console/Commands/Upgrade/UpgradesDatabase.php +++ b/app/Console/Commands/Upgrade/UpgradesDatabase.php @@ -29,6 +29,7 @@ use FireflyIII\Support\Facades\FireflyConfig; use Illuminate\Console\Command; use Illuminate\Support\Facades\Log; use Safe\Exceptions\InfoException; + use function Safe\set_time_limit; try { diff --git a/app/Console/Commands/Upgrade/UpgradesJournalNotes.php b/app/Console/Commands/Upgrade/UpgradesJournalNotes.php index 6ae07abb09..2190cea5e1 100644 --- a/app/Console/Commands/Upgrade/UpgradesJournalNotes.php +++ b/app/Console/Commands/Upgrade/UpgradesJournalNotes.php @@ -87,6 +87,7 @@ class UpgradesJournalNotes extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + return (bool)$configVar?->data; } diff --git a/app/Console/Commands/Upgrade/UpgradesLiabilities.php b/app/Console/Commands/Upgrade/UpgradesLiabilities.php index aaf5f7b221..23b94883f7 100644 --- a/app/Console/Commands/Upgrade/UpgradesLiabilities.php +++ b/app/Console/Commands/Upgrade/UpgradesLiabilities.php @@ -62,6 +62,7 @@ class UpgradesLiabilities extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + return (bool)$configVar?->data; } diff --git a/app/Console/Commands/Upgrade/UpgradesLiabilitiesEight.php b/app/Console/Commands/Upgrade/UpgradesLiabilitiesEight.php index 571fb94ec8..26ecde2584 100644 --- a/app/Console/Commands/Upgrade/UpgradesLiabilitiesEight.php +++ b/app/Console/Commands/Upgrade/UpgradesLiabilitiesEight.php @@ -64,6 +64,7 @@ class UpgradesLiabilitiesEight extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + return (bool)$configVar?->data; } diff --git a/app/Console/Commands/Upgrade/UpgradesMultiPiggyBanks.php b/app/Console/Commands/Upgrade/UpgradesMultiPiggyBanks.php index ecceb1a085..61f7ba4b0e 100644 --- a/app/Console/Commands/Upgrade/UpgradesMultiPiggyBanks.php +++ b/app/Console/Commands/Upgrade/UpgradesMultiPiggyBanks.php @@ -65,6 +65,7 @@ class UpgradesMultiPiggyBanks extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + return (bool)$configVar?->data; } diff --git a/app/Console/Commands/Upgrade/UpgradesPrimaryCurrencyAmounts.php b/app/Console/Commands/Upgrade/UpgradesPrimaryCurrencyAmounts.php index c233272cba..40e768aefc 100644 --- a/app/Console/Commands/Upgrade/UpgradesPrimaryCurrencyAmounts.php +++ b/app/Console/Commands/Upgrade/UpgradesPrimaryCurrencyAmounts.php @@ -61,6 +61,7 @@ class UpgradesPrimaryCurrencyAmounts extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + return (bool)$configVar?->data; } diff --git a/app/Console/Commands/Upgrade/UpgradesRecurrenceMetaData.php b/app/Console/Commands/Upgrade/UpgradesRecurrenceMetaData.php index 7ffd859065..c6126388ec 100644 --- a/app/Console/Commands/Upgrade/UpgradesRecurrenceMetaData.php +++ b/app/Console/Commands/Upgrade/UpgradesRecurrenceMetaData.php @@ -29,6 +29,7 @@ use FireflyIII\Models\Recurrence; use FireflyIII\Models\RecurrenceMeta; use FireflyIII\Models\RecurrenceTransactionMeta; use Illuminate\Console\Command; + use function Safe\json_encode; class UpgradesRecurrenceMetaData extends Command @@ -65,6 +66,7 @@ class UpgradesRecurrenceMetaData extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + return (bool)$configVar?->data; } diff --git a/app/Console/Commands/Upgrade/UpgradesRuleActions.php b/app/Console/Commands/Upgrade/UpgradesRuleActions.php index 7baeb2cbe2..0be7c55811 100644 --- a/app/Console/Commands/Upgrade/UpgradesRuleActions.php +++ b/app/Console/Commands/Upgrade/UpgradesRuleActions.php @@ -64,6 +64,7 @@ class UpgradesRuleActions extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + return (bool)$configVar?->data; } diff --git a/app/Console/Commands/Upgrade/UpgradesTagLocations.php b/app/Console/Commands/Upgrade/UpgradesTagLocations.php index cd60c5b825..c891de9982 100644 --- a/app/Console/Commands/Upgrade/UpgradesTagLocations.php +++ b/app/Console/Commands/Upgrade/UpgradesTagLocations.php @@ -58,6 +58,7 @@ class UpgradesTagLocations extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + return (bool)$configVar?->data; } diff --git a/app/Console/Commands/Upgrade/UpgradesToGroups.php b/app/Console/Commands/Upgrade/UpgradesToGroups.php index 95fbcf339b..c5db2427ca 100644 --- a/app/Console/Commands/Upgrade/UpgradesToGroups.php +++ b/app/Console/Commands/Upgrade/UpgradesToGroups.php @@ -98,6 +98,7 @@ class UpgradesToGroups extends Command private function isMigrated(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + return (bool)$configVar?->data; } diff --git a/app/Console/Commands/Upgrade/UpgradesTransferCurrencies.php b/app/Console/Commands/Upgrade/UpgradesTransferCurrencies.php index 1aced94507..2b4b566c8b 100644 --- a/app/Console/Commands/Upgrade/UpgradesTransferCurrencies.php +++ b/app/Console/Commands/Upgrade/UpgradesTransferCurrencies.php @@ -106,6 +106,7 @@ class UpgradesTransferCurrencies extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + return (bool)$configVar?->data; } diff --git a/app/Console/Commands/Upgrade/UpgradesVariousCurrencyInformation.php b/app/Console/Commands/Upgrade/UpgradesVariousCurrencyInformation.php index 49c4e0cf28..2b47161771 100644 --- a/app/Console/Commands/Upgrade/UpgradesVariousCurrencyInformation.php +++ b/app/Console/Commands/Upgrade/UpgradesVariousCurrencyInformation.php @@ -87,6 +87,7 @@ class UpgradesVariousCurrencyInformation extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + return (bool)$configVar?->data; } diff --git a/app/Console/Commands/Upgrade/UpgradesWebhooks.php b/app/Console/Commands/Upgrade/UpgradesWebhooks.php index 6dd824473a..e917b09596 100644 --- a/app/Console/Commands/Upgrade/UpgradesWebhooks.php +++ b/app/Console/Commands/Upgrade/UpgradesWebhooks.php @@ -64,6 +64,7 @@ class UpgradesWebhooks extends Command private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + return (bool)$configVar?->data; } diff --git a/app/Console/Commands/VerifiesAccessToken.php b/app/Console/Commands/VerifiesAccessToken.php index 2dcb004d35..906980df68 100644 --- a/app/Console/Commands/VerifiesAccessToken.php +++ b/app/Console/Commands/VerifiesAccessToken.php @@ -64,7 +64,6 @@ trait VerifiesAccessToken /** * Returns false when given token does not match given user token. - * */ protected function verifyAccessToken(): bool { diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index f160030059..bc010ba8fa 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -49,6 +49,7 @@ use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Throwable; + use function Safe\json_encode; use function Safe\parse_url; diff --git a/app/Factory/AccountFactory.php b/app/Factory/AccountFactory.php index ce47fd2e35..22df6a483e 100644 --- a/app/Factory/AccountFactory.php +++ b/app/Factory/AccountFactory.php @@ -78,7 +78,8 @@ class AccountFactory if (!$type instanceof AccountType) { throw new FireflyException(sprintf('Cannot find account type "%s"', $accountType)); } - /** @var Account|null $return */ + + /** @var null|Account $return */ $return = $this->user->accounts->where('account_type_id', $type->id)->where('name', $accountName)->first(); if (null === $return) { diff --git a/app/Factory/PiggyBankFactory.php b/app/Factory/PiggyBankFactory.php index 2cf4d35a68..dc6b401311 100644 --- a/app/Factory/PiggyBankFactory.php +++ b/app/Factory/PiggyBankFactory.php @@ -36,6 +36,7 @@ use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\User; use Illuminate\Database\QueryException; use Illuminate\Support\Facades\Log; + use function Safe\json_encode; /** diff --git a/app/Factory/TransactionJournalFactory.php b/app/Factory/TransactionJournalFactory.php index 5f218c5d3f..d11753a2bf 100644 --- a/app/Factory/TransactionJournalFactory.php +++ b/app/Factory/TransactionJournalFactory.php @@ -55,6 +55,7 @@ use FireflyIII\Validation\AccountValidator; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use JsonException; + use function Safe\json_encode; /** @@ -342,8 +343,6 @@ class TransactionJournalFactory /** * If this transaction already exists, throw an error. * - * @param string $hash - * * @throws DuplicateTransactionException * @throws JsonException * @throws \Safe\Exceptions\JsonException @@ -480,8 +479,6 @@ class TransactionJournalFactory }; } - /** - */ private function getCurrency(?TransactionCurrency $currency, Account $account): TransactionCurrency { Log::debug(sprintf('Now in getCurrency(#%d, "%s")', $currency?->id, $account->name)); diff --git a/app/Handlers/Events/BillEventHandler.php b/app/Handlers/Events/BillEventHandler.php index aef9cb49d2..9969bfbf1c 100644 --- a/app/Handlers/Events/BillEventHandler.php +++ b/app/Handlers/Events/BillEventHandler.php @@ -33,6 +33,7 @@ use FireflyIII\Notifications\User\SubscriptionsOverdueReminder; use FireflyIII\Support\Facades\Preferences; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Notification; + use function Safe\json_encode; /** diff --git a/app/Handlers/Events/UserEventHandler.php b/app/Handlers/Events/UserEventHandler.php index 39271d86a2..db1fdd4134 100644 --- a/app/Handlers/Events/UserEventHandler.php +++ b/app/Handlers/Events/UserEventHandler.php @@ -164,7 +164,6 @@ class UserEventHandler /** * Set the demo user back to English. - * */ public function demoUserBackToEnglish(Login $event): void { @@ -181,8 +180,6 @@ class UserEventHandler } } - /** - */ public function notifyNewIPAddress(DetectedNewIPAddress $event): void { $user = $event->user; @@ -445,8 +442,6 @@ class UserEventHandler Log::debug(sprintf('If you see no errors above this line, test notification was sent over channel "%s"', $event->channel)); } - /** - */ public function storeUserIPAddress(ActuallyLoggedIn $event): void { app('log')->debug('Now in storeUserIPAddress'); diff --git a/app/Handlers/Events/VersionCheckEventHandler.php b/app/Handlers/Events/VersionCheckEventHandler.php index 2087d37f76..3a2d7c42d4 100644 --- a/app/Handlers/Events/VersionCheckEventHandler.php +++ b/app/Handlers/Events/VersionCheckEventHandler.php @@ -44,8 +44,6 @@ class VersionCheckEventHandler /** * Checks with GitHub to see if there is a new version. * - * @param RequestedVersionCheckStatus $event - * * @throws ContainerExceptionInterface * @throws FireflyException * @throws NotFoundExceptionInterface @@ -93,8 +91,6 @@ class VersionCheckEventHandler } /** - * @param RequestedVersionCheckStatus $event - * * @throws FireflyException * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface diff --git a/app/Helpers/Attachments/AttachmentHelper.php b/app/Helpers/Attachments/AttachmentHelper.php index ef0cf558d2..c4d79ed8d2 100644 --- a/app/Helpers/Attachments/AttachmentHelper.php +++ b/app/Helpers/Attachments/AttachmentHelper.php @@ -38,11 +38,13 @@ use Safe\Exceptions\FileinfoException; use Safe\Exceptions\FilesystemException; use Safe\Exceptions\StringsException; use Symfony\Component\HttpFoundation\File\UploadedFile; + use function Safe\fclose; use function Safe\finfo_open; use function Safe\fwrite; use function Safe\md5_file; use function Safe\tmpfile; + use const DIRECTORY_SEPARATOR; /** @@ -220,10 +222,6 @@ class AttachmentHelper implements AttachmentHelperInterface /** * Process the upload of a file. * - * @param UploadedFile $file - * @param Model $model - * - * @return Attachment|null * @throws StringsException */ protected function processFile(UploadedFile $file, Model $model): ?Attachment diff --git a/app/Helpers/Collector/Extensions/MetaCollection.php b/app/Helpers/Collector/Extensions/MetaCollection.php index 40185ef81c..11a15ef799 100644 --- a/app/Helpers/Collector/Extensions/MetaCollection.php +++ b/app/Helpers/Collector/Extensions/MetaCollection.php @@ -36,6 +36,7 @@ use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; + use function Safe\json_encode; /** diff --git a/app/Helpers/Collector/GroupCollector.php b/app/Helpers/Collector/GroupCollector.php index db4834fbf2..7a44ba5737 100644 --- a/app/Helpers/Collector/GroupCollector.php +++ b/app/Helpers/Collector/GroupCollector.php @@ -48,6 +48,7 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use Override; use Safe\Exceptions\JsonException; + use function Safe\json_decode; /** @@ -564,9 +565,6 @@ class GroupCollector implements GroupCollectorInterface } /** - * @param TransactionJournal $augumentedJournal - * - * @return array * @throws FireflyException * @throws JsonException */ diff --git a/app/Helpers/Fiscal/FiscalHelper.php b/app/Helpers/Fiscal/FiscalHelper.php index ae7ebc752b..53aad1fef3 100644 --- a/app/Helpers/Fiscal/FiscalHelper.php +++ b/app/Helpers/Fiscal/FiscalHelper.php @@ -64,9 +64,8 @@ class FiscalHelper implements FiscalHelperInterface } /** - * @param Carbon $date - * * @return Carbon date object + * * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ diff --git a/app/Helpers/Webhook/Sha3SignatureGenerator.php b/app/Helpers/Webhook/Sha3SignatureGenerator.php index ce6a0680f7..06967cff04 100644 --- a/app/Helpers/Webhook/Sha3SignatureGenerator.php +++ b/app/Helpers/Webhook/Sha3SignatureGenerator.php @@ -28,6 +28,7 @@ use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\WebhookMessage; use JsonException; + use function Safe\json_encode; /** diff --git a/app/Http/Controllers/Account/CreateController.php b/app/Http/Controllers/Account/CreateController.php index e884c7c5cc..bab54065d9 100644 --- a/app/Http/Controllers/Account/CreateController.php +++ b/app/Http/Controllers/Account/CreateController.php @@ -132,8 +132,6 @@ class CreateController extends Controller /** * Store the new account. * - * @param AccountFormRequest $request - * * @return Redirector|RedirectResponse * * @throws ContainerExceptionInterface diff --git a/app/Http/Controllers/Account/IndexController.php b/app/Http/Controllers/Account/IndexController.php index 8d35f6b56e..4f34f0dd5f 100644 --- a/app/Http/Controllers/Account/IndexController.php +++ b/app/Http/Controllers/Account/IndexController.php @@ -67,9 +67,6 @@ class IndexController extends Controller } /** - * @param Request $request - * @param string $objectType - * * @return Factory|View * * @throws ContainerExceptionInterface @@ -141,9 +138,6 @@ class IndexController extends Controller /** * Show list of accounts. * - * @param Request $request - * @param string $objectType - * * @return Factory|View * * @throws ContainerExceptionInterface diff --git a/app/Http/Controllers/Account/ShowController.php b/app/Http/Controllers/Account/ShowController.php index 8181e0ff80..9b1a6922f6 100644 --- a/app/Http/Controllers/Account/ShowController.php +++ b/app/Http/Controllers/Account/ShowController.php @@ -78,11 +78,6 @@ class ShowController extends Controller /** * Show an account. * - * @param Request $request - * @param Account $account - * @param Carbon|null $start - * @param Carbon|null $end - * * @return Factory|Redirector|RedirectResponse|View * * @throws ContainerExceptionInterface @@ -202,9 +197,6 @@ class ShowController extends Controller /** * Show an account. * - * @param Request $request - * @param Account $account - * * @return Factory|Redirector|RedirectResponse|View * * @throws ContainerExceptionInterface diff --git a/app/Http/Controllers/Admin/HomeController.php b/app/Http/Controllers/Admin/HomeController.php index 264d79158d..bd14a9d51b 100644 --- a/app/Http/Controllers/Admin/HomeController.php +++ b/app/Http/Controllers/Admin/HomeController.php @@ -49,6 +49,7 @@ class HomeController extends Controller * Index of the admin. * * @return Factory|View + * * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 54ad535864..736ae8edf7 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -154,6 +154,7 @@ class UserController extends Controller * Show index of user manager. * * @return Factory|View + * * @throws ContainerExceptionInterface * @throws FireflyException * @throws NotFoundExceptionInterface diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php index c9d54c0982..a945fc7879 100644 --- a/app/Http/Controllers/Auth/ForgotPasswordController.php +++ b/app/Http/Controllers/Auth/ForgotPasswordController.php @@ -36,6 +36,7 @@ use Illuminate\View\View; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Safe\Exceptions\UrlException; + use function Safe\parse_url; /** @@ -61,10 +62,8 @@ class ForgotPasswordController extends Controller /** * Send a reset link to the given user. * - * @param Request $request - * @param UserRepositoryInterface $repository - * * @return Factory|RedirectResponse|View + * * @throws FireflyException */ public function sendResetLinkEmail(Request $request, UserRepositoryInterface $repository) diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 6e4bba5669..30b0bce73f 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -221,8 +221,6 @@ class LoginController extends Controller /** * Show the application's login form. * - * @param Request $request - * * @return Application|Factory|Redirector|RedirectResponse|View * * @throws FireflyException diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php index 4122f3fa5a..a36e65e8bc 100644 --- a/app/Http/Controllers/Auth/RegisterController.php +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -140,9 +140,6 @@ class RegisterController extends Controller /** * Show the application registration form if the invitation code is valid. * - * @param Request $request - * @param string $code - * * @return Factory|View * * @throws ContainerExceptionInterface @@ -177,8 +174,6 @@ class RegisterController extends Controller /** * Show the application registration form. * - * @param Request|null $request - * * @return Factory|View * * @throws ContainerExceptionInterface diff --git a/app/Http/Controllers/Auth/ResetPasswordController.php b/app/Http/Controllers/Auth/ResetPasswordController.php index 7ff28e0264..714d98bf32 100644 --- a/app/Http/Controllers/Auth/ResetPasswordController.php +++ b/app/Http/Controllers/Auth/ResetPasswordController.php @@ -114,8 +114,7 @@ class ResetPasswordController extends Controller * * If no token is present, display the link request form. * - * @param Request $request - * @param null $token + * @param null $token * * @return Factory|View * diff --git a/app/Http/Controllers/Auth/TwoFactorController.php b/app/Http/Controllers/Auth/TwoFactorController.php index d9cd52c035..a651b7c50a 100644 --- a/app/Http/Controllers/Auth/TwoFactorController.php +++ b/app/Http/Controllers/Auth/TwoFactorController.php @@ -61,9 +61,8 @@ class TwoFactorController extends Controller } /** - * @param Request $request - * * @return Redirector|RedirectResponse + * * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ diff --git a/app/Http/Controllers/Bill/IndexController.php b/app/Http/Controllers/Bill/IndexController.php index 3896db77af..fcc0ebeb0d 100644 --- a/app/Http/Controllers/Bill/IndexController.php +++ b/app/Http/Controllers/Bill/IndexController.php @@ -147,8 +147,6 @@ class IndexController extends Controller return view('bills.index', compact('bills', 'sums', 'total', 'totals', 'today')); } - /** - */ private function getSums(array $bills): array { $sums = []; diff --git a/app/Http/Controllers/Bill/ShowController.php b/app/Http/Controllers/Bill/ShowController.php index ef004709f5..f102f5b5ac 100644 --- a/app/Http/Controllers/Bill/ShowController.php +++ b/app/Http/Controllers/Bill/ShowController.php @@ -115,10 +115,8 @@ class ShowController extends Controller /** * Show a bill. * - * @param Request $request - * @param Bill $bill - * * @return Factory|View + * * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ diff --git a/app/Http/Controllers/Budget/ShowController.php b/app/Http/Controllers/Budget/ShowController.php index 77158fb699..4c31d2927a 100644 --- a/app/Http/Controllers/Budget/ShowController.php +++ b/app/Http/Controllers/Budget/ShowController.php @@ -75,10 +75,6 @@ class ShowController extends Controller /** * Show transactions without a budget. * - * @param Request $request - * @param Carbon|null $start - * @param Carbon|null $end - * * @return Factory|View * * @throws ContainerExceptionInterface @@ -118,9 +114,8 @@ class ShowController extends Controller /** * Shows ALL transactions without a budget. * - * @param Request $request - * * @return Factory|View + * * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ @@ -147,10 +142,8 @@ class ShowController extends Controller /** * Show a single budget. * - * @param Request $request - * @param Budget $budget - * * @return Factory|View + * * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ @@ -183,10 +176,6 @@ class ShowController extends Controller /** * Show a single budget by a budget limit. * - * @param Request $request - * @param Budget $budget - * @param BudgetLimit $budgetLimit - * * @return Factory|View * * @throws FireflyException diff --git a/app/Http/Controllers/Category/IndexController.php b/app/Http/Controllers/Category/IndexController.php index ecd406df91..f91870a346 100644 --- a/app/Http/Controllers/Category/IndexController.php +++ b/app/Http/Controllers/Category/IndexController.php @@ -64,9 +64,8 @@ class IndexController extends Controller /** * Show all categories. * - * @param Request $request - * * @return Factory|View + * * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ diff --git a/app/Http/Controllers/Category/NoCategoryController.php b/app/Http/Controllers/Category/NoCategoryController.php index 065214b856..b2dade990d 100644 --- a/app/Http/Controllers/Category/NoCategoryController.php +++ b/app/Http/Controllers/Category/NoCategoryController.php @@ -71,10 +71,6 @@ class NoCategoryController extends Controller /** * Show transactions without a category. * - * @param Request $request - * @param Carbon|null $start - * @param Carbon|null $end - * * @return Factory|View * * @throws ContainerExceptionInterface @@ -114,9 +110,8 @@ class NoCategoryController extends Controller /** * Show all transactions without a category. * - * @param Request $request - * * @return Factory|View + * * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ diff --git a/app/Http/Controllers/Category/ShowController.php b/app/Http/Controllers/Category/ShowController.php index c9ca74b93c..57fa33fc7a 100644 --- a/app/Http/Controllers/Category/ShowController.php +++ b/app/Http/Controllers/Category/ShowController.php @@ -70,11 +70,6 @@ class ShowController extends Controller /** * Show a single category. * - * @param Request $request - * @param Category $category - * @param Carbon|null $start - * @param Carbon|null $end - * * @return Factory|View * * @throws ContainerExceptionInterface @@ -120,10 +115,8 @@ class ShowController extends Controller /** * Show all transactions within a category. * - * @param Request $request - * @param Category $category - * * @return Factory|View + * * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ diff --git a/app/Http/Controllers/Chart/AccountController.php b/app/Http/Controllers/Chart/AccountController.php index 9e0e34851c..3df8e35845 100644 --- a/app/Http/Controllers/Chart/AccountController.php +++ b/app/Http/Controllers/Chart/AccountController.php @@ -45,6 +45,7 @@ use Illuminate\Http\JsonResponse; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use Safe\Exceptions\JsonException; + use function Safe\json_encode; /** @@ -482,11 +483,6 @@ class AccountController extends Controller /** * Shows overview of account during a single period. * - * @param Account $account - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse * @throws FireflyException * @throws JsonException */ diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 1d3665b548..6e195311b3 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -66,7 +66,6 @@ class CategoryController extends Controller /** * Show an overview for a category for all time, per month/week/year. * TODO test method, for category refactor. - * */ public function all(Category $category): JsonResponse { @@ -256,10 +255,6 @@ class CategoryController extends Controller * Chart for a specific period. * TODO test me, for category refactor. * - * @param Category $category - * @param Carbon $date - * - * @return JsonResponse * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ diff --git a/app/Http/Controllers/Chart/PiggyBankController.php b/app/Http/Controllers/Chart/PiggyBankController.php index 065cc16f49..c0db48f026 100644 --- a/app/Http/Controllers/Chart/PiggyBankController.php +++ b/app/Http/Controllers/Chart/PiggyBankController.php @@ -57,7 +57,6 @@ class PiggyBankController extends Controller * Shows the piggy bank history. * * TODO this chart is not multi currency aware. - * */ public function history(PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank): JsonResponse { diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index daba3dbeae..534acf8488 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -38,6 +38,7 @@ use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\View; + use function Safe\ini_get; use function Safe\realpath; diff --git a/app/Http/Controllers/DebugController.php b/app/Http/Controllers/DebugController.php index f83dfb0f37..1ed00b4710 100644 --- a/app/Http/Controllers/DebugController.php +++ b/app/Http/Controllers/DebugController.php @@ -51,8 +51,10 @@ use Illuminate\View\View; use Monolog\Handler\RotatingFileHandler; use Safe\Exceptions\FilesystemException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; + use function Safe\file_get_contents; use function Safe\ini_get; + use const PHP_INT_SIZE; use const PHP_SAPI; diff --git a/app/Http/Controllers/JavascriptController.php b/app/Http/Controllers/JavascriptController.php index 94696f0373..c5beecfeb4 100644 --- a/app/Http/Controllers/JavascriptController.php +++ b/app/Http/Controllers/JavascriptController.php @@ -92,10 +92,6 @@ class JavascriptController extends Controller /** * Show some common variables to be used in scripts. * - * @param Request $request - * @param AccountRepositoryInterface $repository - * - * @return Response * @throws FireflyException * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface diff --git a/app/Http/Controllers/Json/IntroController.php b/app/Http/Controllers/Json/IntroController.php index 8d1aa947a8..7a55bca8c1 100644 --- a/app/Http/Controllers/Json/IntroController.php +++ b/app/Http/Controllers/Json/IntroController.php @@ -87,7 +87,6 @@ class IntroController extends Controller /** * Enable the boxes for a specific page again. - * */ public function postEnable(string $route, ?string $specialPage = null): JsonResponse { @@ -106,7 +105,6 @@ class IntroController extends Controller /** * Set that you saw them. - * */ public function postFinished(string $route, ?string $specialPage = null): JsonResponse { diff --git a/app/Http/Controllers/NewUserController.php b/app/Http/Controllers/NewUserController.php index e1c70c78f4..5bfbc6815e 100644 --- a/app/Http/Controllers/NewUserController.php +++ b/app/Http/Controllers/NewUserController.php @@ -85,7 +85,6 @@ class NewUserController extends Controller * Store his new settings. * * @return Redirector|RedirectResponse - * */ public function submit(NewUserFormRequest $request, CurrencyRepositoryInterface $currencyRepository) { diff --git a/app/Http/Controllers/PiggyBank/IndexController.php b/app/Http/Controllers/PiggyBank/IndexController.php index 199d90a3a8..60c089fee0 100644 --- a/app/Http/Controllers/PiggyBank/IndexController.php +++ b/app/Http/Controllers/PiggyBank/IndexController.php @@ -76,7 +76,6 @@ class IndexController extends Controller * TODO very complicated function. * * @return Factory|View - * */ public function index() { diff --git a/app/Http/Controllers/PreferencesController.php b/app/Http/Controllers/PreferencesController.php index 723998cd3f..f1c2ac8658 100644 --- a/app/Http/Controllers/PreferencesController.php +++ b/app/Http/Controllers/PreferencesController.php @@ -43,6 +43,7 @@ use Illuminate\Support\Facades\Log; use Illuminate\View\View; use JsonException; use Safe\Exceptions\FilesystemException; + use function Safe\file_get_contents; use function Safe\json_decode; @@ -71,8 +72,6 @@ class PreferencesController extends Controller /** * Show overview of preferences. * - * @param AccountRepositoryInterface $repository - * * @return Factory|View * * @throws FireflyException diff --git a/app/Http/Controllers/Profile/MfaController.php b/app/Http/Controllers/Profile/MfaController.php index 7959f1cd54..123680e2c3 100644 --- a/app/Http/Controllers/Profile/MfaController.php +++ b/app/Http/Controllers/Profile/MfaController.php @@ -84,8 +84,6 @@ class MfaController extends Controller } - /** - */ public function backupCodes(Request $request): Factory|RedirectResponse|View { if (!$this->internalAuth) { @@ -230,8 +228,6 @@ class MfaController extends Controller /** * Submit 2FA for the first time. * - * @param TokenFormRequest $request - * * @return Redirector|RedirectResponse * * @throws ContainerExceptionInterface @@ -293,8 +289,6 @@ class MfaController extends Controller /** * TODO duplicate code. * - * @param string $mfaCode - * * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 33f8b41866..26e1509103 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -138,7 +138,6 @@ class ProfileController extends Controller /** * Index for profile. * - * @return Factory|View * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ diff --git a/app/Http/Controllers/Recurring/IndexController.php b/app/Http/Controllers/Recurring/IndexController.php index 9923a42d0a..be4d9e527c 100644 --- a/app/Http/Controllers/Recurring/IndexController.php +++ b/app/Http/Controllers/Recurring/IndexController.php @@ -73,8 +73,6 @@ class IndexController extends Controller * TODO the notes of a recurrence are pretty pointless at this moment. * Show all recurring transactions. * - * @param Request $request - * * @return Factory|View * * @throws FireflyException diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index 126d312f71..eb82e859d9 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -227,9 +227,8 @@ class ReportController extends Controller /** * Show index. * - * @param AccountRepositoryInterface $repository - * * @return Factory|View + * * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ diff --git a/app/Http/Controllers/System/InstallController.php b/app/Http/Controllers/System/InstallController.php index 48ff8a64b0..6e1e168d0a 100644 --- a/app/Http/Controllers/System/InstallController.php +++ b/app/Http/Controllers/System/InstallController.php @@ -37,6 +37,7 @@ use Illuminate\Support\Facades\Cache; use Illuminate\View\View; use Laravel\Passport\Passport; use phpseclib3\Crypt\RSA; + use function Safe\file_put_contents; /** diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index d062801d47..53372408ae 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -220,11 +220,6 @@ class TagController extends Controller /** * Show a single tag. * - * @param Request $request - * @param Tag $tag - * @param Carbon|null $start - * @param Carbon|null $end - * * @return Factory|View * * @throws ContainerExceptionInterface @@ -270,10 +265,8 @@ class TagController extends Controller /** * Show a single tag over all time. * - * @param Request $request - * @param Tag $tag - * * @return Factory|View + * * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ diff --git a/app/Http/Controllers/Transaction/CreateController.php b/app/Http/Controllers/Transaction/CreateController.php index 71012d3565..054a94ec23 100644 --- a/app/Http/Controllers/Transaction/CreateController.php +++ b/app/Http/Controllers/Transaction/CreateController.php @@ -38,6 +38,7 @@ use Illuminate\Http\Request; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Safe\Exceptions\UrlException; + use function Safe\parse_url; /** @@ -99,8 +100,6 @@ class CreateController extends Controller /** * Create a new transaction group. * - * @param string|null $objectType - * * @return Factory|View * * @throws ContainerExceptionInterface diff --git a/app/Http/Controllers/Transaction/EditController.php b/app/Http/Controllers/Transaction/EditController.php index dcf9e36ca3..d349a9e18c 100644 --- a/app/Http/Controllers/Transaction/EditController.php +++ b/app/Http/Controllers/Transaction/EditController.php @@ -37,6 +37,7 @@ use Illuminate\View\View; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Safe\Exceptions\UrlException; + use function Safe\parse_url; /** @@ -67,9 +68,8 @@ class EditController extends Controller } /** - * @param TransactionGroup $transactionGroup - * * @return Factory|Redirector|RedirectResponse|View + * * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface * @throws UrlException diff --git a/app/Http/Controllers/Transaction/IndexController.php b/app/Http/Controllers/Transaction/IndexController.php index e0d2c83359..efb29a1236 100644 --- a/app/Http/Controllers/Transaction/IndexController.php +++ b/app/Http/Controllers/Transaction/IndexController.php @@ -69,11 +69,6 @@ class IndexController extends Controller /** * Index for a range of transactions. * - * @param Request $request - * @param string $objectType - * @param Carbon|null $start - * @param Carbon|null $end - * * @return Factory|View * * @throws ContainerExceptionInterface @@ -132,10 +127,8 @@ class IndexController extends Controller /** * Index for ALL transactions. * - * @param Request $request - * @param string $objectType - * * @return Factory|View + * * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ diff --git a/app/Http/Controllers/TransactionCurrency/DeleteController.php b/app/Http/Controllers/TransactionCurrency/DeleteController.php index ca25c9c2e5..7a2348cdff 100644 --- a/app/Http/Controllers/TransactionCurrency/DeleteController.php +++ b/app/Http/Controllers/TransactionCurrency/DeleteController.php @@ -67,10 +67,8 @@ class DeleteController extends Controller /** * Deletes a currency. * - * @param Request $request - * @param TransactionCurrency $currency - * * @return Factory|Redirector|RedirectResponse|View + * * @throws FireflyException */ public function delete(Request $request, TransactionCurrency $currency) @@ -104,10 +102,8 @@ class DeleteController extends Controller /** * Destroys a currency. * - * @param Request $request - * @param TransactionCurrency $currency - * * @return Redirector|RedirectResponse + * * @throws FireflyException */ public function destroy(Request $request, TransactionCurrency $currency) diff --git a/app/Http/Controllers/TransactionCurrency/EditController.php b/app/Http/Controllers/TransactionCurrency/EditController.php index 092b5c623f..d079b5ec8e 100644 --- a/app/Http/Controllers/TransactionCurrency/EditController.php +++ b/app/Http/Controllers/TransactionCurrency/EditController.php @@ -107,10 +107,8 @@ class EditController extends Controller /** * Updates a currency. * - * @param CurrencyFormRequest $request - * @param TransactionCurrency $currency - * * @return Redirector|RedirectResponse + * * @throws FireflyException */ public function update(CurrencyFormRequest $request, TransactionCurrency $currency) diff --git a/app/Http/Controllers/TransactionCurrency/IndexController.php b/app/Http/Controllers/TransactionCurrency/IndexController.php index 62566bbd3b..a24f1123d2 100644 --- a/app/Http/Controllers/TransactionCurrency/IndexController.php +++ b/app/Http/Controllers/TransactionCurrency/IndexController.php @@ -63,9 +63,8 @@ class IndexController extends Controller /** * Show overview of currencies. * - * @param Request $request - * * @return Factory|View + * * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ diff --git a/app/Http/Middleware/AcceptHeaders.php b/app/Http/Middleware/AcceptHeaders.php index c8b8749ad8..9cd43dc583 100644 --- a/app/Http/Middleware/AcceptHeaders.php +++ b/app/Http/Middleware/AcceptHeaders.php @@ -29,6 +29,7 @@ use Illuminate\Http\Request; use Illuminate\Http\Response; use Safe\Exceptions\PcreException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; + use function Safe\preg_match; class AcceptHeaders @@ -36,9 +37,6 @@ class AcceptHeaders /** * Handle the incoming requests. * - * @param Request $request - * @param callable $next - * * @return Response * * @throws BadHttpHeaderException diff --git a/app/Http/Requests/ReportFormRequest.php b/app/Http/Requests/ReportFormRequest.php index fd11a32096..eda973b3fb 100644 --- a/app/Http/Requests/ReportFormRequest.php +++ b/app/Http/Requests/ReportFormRequest.php @@ -36,6 +36,7 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; use Safe\Exceptions\PcreException; + use function Safe\preg_match; /** @@ -133,7 +134,6 @@ class ReportFormRequest extends FormRequest /** * Validate end date. * - * @return Carbon * @throws FireflyException * @throws PcreException */ @@ -173,7 +173,6 @@ class ReportFormRequest extends FormRequest /** * Validate start date. * - * @return Carbon * @throws FireflyException * @throws PcreException */ diff --git a/app/Jobs/DownloadExchangeRates.php b/app/Jobs/DownloadExchangeRates.php index 9b29bcaac5..e5be322d09 100644 --- a/app/Jobs/DownloadExchangeRates.php +++ b/app/Jobs/DownloadExchangeRates.php @@ -41,6 +41,7 @@ use Illuminate\Queue\SerializesModels; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use Safe\Exceptions\JsonException; + use function Safe\json_decode; /** @@ -94,8 +95,6 @@ class DownloadExchangeRates implements ShouldQueue } /** - * @param TransactionCurrency $currency - * * @throws GuzzleException * @throws JsonException */ diff --git a/app/Jobs/MailError.php b/app/Jobs/MailError.php index 70c7f407b4..ecc8c24b5d 100644 --- a/app/Jobs/MailError.php +++ b/app/Jobs/MailError.php @@ -32,6 +32,7 @@ use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Mail; use Symfony\Component\Mailer\Exception\TransportException; + use function Safe\file_get_contents; use function Safe\file_put_contents; use function Safe\json_decode; diff --git a/app/Mail/InvitationMail.php b/app/Mail/InvitationMail.php index 0c886bc21e..14ad8821b5 100644 --- a/app/Mail/InvitationMail.php +++ b/app/Mail/InvitationMail.php @@ -27,6 +27,7 @@ namespace FireflyIII\Mail; use Illuminate\Bus\Queueable; use Illuminate\Mail\Mailable; use Illuminate\Queue\SerializesModels; + use function Safe\parse_url; /** diff --git a/app/Mail/ReportNewJournalsMail.php b/app/Mail/ReportNewJournalsMail.php index 0fd9ac7127..c8b51a782d 100644 --- a/app/Mail/ReportNewJournalsMail.php +++ b/app/Mail/ReportNewJournalsMail.php @@ -52,6 +52,7 @@ class ReportNewJournalsMail extends Mailable * Build the message. * * @return $this + * * @throws FireflyException */ public function build(): self diff --git a/app/Models/AccountMeta.php b/app/Models/AccountMeta.php index ff42cb44f7..a91e5eb778 100644 --- a/app/Models/AccountMeta.php +++ b/app/Models/AccountMeta.php @@ -27,6 +27,7 @@ use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; + use function Safe\json_decode; use function Safe\json_encode; diff --git a/app/Models/BudgetLimit.php b/app/Models/BudgetLimit.php index f29c7102d1..6ac0d049f3 100644 --- a/app/Models/BudgetLimit.php +++ b/app/Models/BudgetLimit.php @@ -58,9 +58,10 @@ class BudgetLimit extends Model if (auth()->check()) { $budgetLimitId = (int)$value; $budgetLimit = self::where('budget_limits.id', $budgetLimitId) - ->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') - ->where('budgets.user_id', auth()->user()->id) - ->first(['budget_limits.*']); + ->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') + ->where('budgets.user_id', auth()->user()->id) + ->first(['budget_limits.*']) + ; if (null !== $budgetLimit) { return $budgetLimit; } @@ -93,14 +94,14 @@ class BudgetLimit extends Model protected function amount(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } protected function budgetId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } @@ -120,7 +121,7 @@ class BudgetLimit extends Model protected function transactionCurrencyId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index e1fe1be20f..3fd2d82a19 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -27,6 +27,7 @@ use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; + use function Safe\json_decode; use function Safe\json_encode; diff --git a/app/Models/Recurrence.php b/app/Models/Recurrence.php index 306be4dde4..1893fa023e 100644 --- a/app/Models/Recurrence.php +++ b/app/Models/Recurrence.php @@ -40,8 +40,8 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** - * @property Carbon $first_date - * @property Carbon|null $latest_date + * @property Carbon $first_date + * @property null|Carbon $latest_date */ #[ObservedBy([RecurrenceObserver::class])] class Recurrence extends Model diff --git a/app/Models/TransactionGroup.php b/app/Models/TransactionGroup.php index 544cdd3830..ee6911ee37 100644 --- a/app/Models/TransactionGroup.php +++ b/app/Models/TransactionGroup.php @@ -36,10 +36,9 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; - /** - * @property User $user - * @property UserGroup $userGroup + * @property User $user + * @property UserGroup $userGroup * @property Collection $transactionJournals */ #[ObservedBy([TransactionGroupObserver::class])] diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index 72e1cd35cc..f6bd140648 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -47,6 +47,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method EloquentBuilder|static before() * @method EloquentBuilder|static after() * @method static EloquentBuilder|static query() + * * @property TransactionGroup $transactionGroup */ #[ObservedBy([TransactionJournalObserver::class])] diff --git a/app/Models/TransactionJournalMeta.php b/app/Models/TransactionJournalMeta.php index 8c2c02ad72..4a748bfb8b 100644 --- a/app/Models/TransactionJournalMeta.php +++ b/app/Models/TransactionJournalMeta.php @@ -28,6 +28,7 @@ use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; + use function Safe\json_decode; use function Safe\json_encode; diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index e28f83d871..263342b753 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -30,6 +30,7 @@ use Illuminate\Support\Facades\Schema; use Illuminate\Support\ServiceProvider; use Laravel\Passport\Passport; use Override; + use function Safe\preg_match; /** diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index 4b0278de7d..8eacb438b4 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -48,6 +48,7 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; use Override; + use function Safe\json_encode; /** diff --git a/app/Repositories/Account/AccountTasker.php b/app/Repositories/Account/AccountTasker.php index a9ca59cb65..63260b6643 100644 --- a/app/Repositories/Account/AccountTasker.php +++ b/app/Repositories/Account/AccountTasker.php @@ -143,8 +143,6 @@ class AccountTasker implements AccountTaskerInterface, UserGroupInterface return $report; } - /** - */ private function groupExpenseByDestination(array $array): array { $primaryCurrency = app('amount')->getPrimaryCurrencyByUserGroup($this->user->userGroup); @@ -230,8 +228,6 @@ class AccountTasker implements AccountTaskerInterface, UserGroupInterface return $report; } - /** - */ private function groupIncomeBySource(array $array): array { $primaryCurrency = app('amount')->getPrimaryCurrencyByUserGroup($this->user->userGroup); diff --git a/app/Repositories/Budget/AvailableBudgetRepository.php b/app/Repositories/Budget/AvailableBudgetRepository.php index cc2916cb92..e992bff82d 100644 --- a/app/Repositories/Budget/AvailableBudgetRepository.php +++ b/app/Repositories/Budget/AvailableBudgetRepository.php @@ -113,7 +113,7 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface, U ; } - #[\Deprecated] + #[Deprecated] public function getAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end): string { $amount = '0'; diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 23cc13e273..0b2d41395d 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -386,8 +386,6 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface return $budget->autoBudgets()->first(); } - /** - */ private function updateAutoBudget(Budget $budget, array $data): void { // update or create auto-budget: @@ -524,6 +522,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface public function firstUseDate(Budget $budget): ?Carbon { $journal = $budget->transactionJournals()->orderBy('date', 'ASC')->first(); + return $journal?->date; } @@ -564,6 +563,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface public function getNoteText(Budget $budget): ?string { $note = $budget->notes()->first(); + return $note?->text; } diff --git a/app/Repositories/Budget/NoBudgetRepository.php b/app/Repositories/Budget/NoBudgetRepository.php index 8ae96cb192..3b5709b18b 100644 --- a/app/Repositories/Budget/NoBudgetRepository.php +++ b/app/Repositories/Budget/NoBudgetRepository.php @@ -33,6 +33,7 @@ use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use Illuminate\Support\Collection; use Override; +use Deprecated; /** * Class NoBudgetRepository @@ -41,7 +42,7 @@ class NoBudgetRepository implements NoBudgetRepositoryInterface, UserGroupInterf { use UserGroupTrait; - #[\Deprecated] + #[Deprecated] public function getNoBudgetPeriodReport(Collection $accounts, Carbon $start, Carbon $end): array { $carbonFormat = app('navigation')->preferredCarbonFormat($start, $end); diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 7e46ff30d9..6b1f75a91c 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -261,6 +261,7 @@ class CategoryRepository implements CategoryRepositoryInterface, UserGroupInterf public function getNoteText(Category $category): ?string { $dbNote = $category->notes()->first(); + return $dbNote?->text; } diff --git a/app/Repositories/Currency/CurrencyRepository.php b/app/Repositories/Currency/CurrencyRepository.php index a3f673f108..eacd820c34 100644 --- a/app/Repositories/Currency/CurrencyRepository.php +++ b/app/Repositories/Currency/CurrencyRepository.php @@ -45,6 +45,7 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use Override; use Safe\Exceptions\JsonException; + use function Safe\json_encode; /** @@ -65,9 +66,6 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf } /** - * @param TransactionCurrency $currency - * - * @return string|null * @throws JsonException */ public function currencyInUseAt(TransactionCurrency $currency): ?string @@ -236,7 +234,6 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf /** * Find by object, ID or code. Returns user default or system default. - * */ public function findCurrency(?int $currencyId, ?string $currencyCode): TransactionCurrency { diff --git a/app/Repositories/Journal/JournalCLIRepository.php b/app/Repositories/Journal/JournalCLIRepository.php index dd47a39323..eaaaf600b4 100644 --- a/app/Repositories/Journal/JournalCLIRepository.php +++ b/app/Repositories/Journal/JournalCLIRepository.php @@ -166,6 +166,7 @@ class JournalCLIRepository implements JournalCLIRepositoryInterface, UserGroupIn public function getNoteText(TransactionJournal $journal): ?string { $note = $journal->notes()->first(); + return $note?->text; } diff --git a/app/Repositories/Recurring/RecurringRepository.php b/app/Repositories/Recurring/RecurringRepository.php index c519ab397f..4e8a67ec0d 100644 --- a/app/Repositories/Recurring/RecurringRepository.php +++ b/app/Repositories/Recurring/RecurringRepository.php @@ -50,6 +50,7 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; + use function Safe\json_decode; use function Safe\json_encode; @@ -429,7 +430,6 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte /** * Parse the repetition in a string that is user readable. - * */ public function repetitionDescription(RecurrenceRepetition $repetition): string { diff --git a/app/Repositories/TransactionGroup/TransactionGroupRepository.php b/app/Repositories/TransactionGroup/TransactionGroupRepository.php index 8e8eac95e3..fbf45efb73 100644 --- a/app/Repositories/TransactionGroup/TransactionGroupRepository.php +++ b/app/Repositories/TransactionGroup/TransactionGroupRepository.php @@ -49,6 +49,7 @@ use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; + use function Safe\json_decode; /** @@ -176,6 +177,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface, ->where('noteable_type', TransactionJournal::class) ->first() ; + return $note?->text; } diff --git a/app/Repositories/User/UserRepository.php b/app/Repositories/User/UserRepository.php index d2e5c3fa81..da82fe44ad 100644 --- a/app/Repositories/User/UserRepository.php +++ b/app/Repositories/User/UserRepository.php @@ -161,6 +161,7 @@ class UserRepository implements UserRepositoryInterface { /** @var null|Role $role */ $role = $user->roles()->first(); + return $role?->name; } @@ -376,7 +377,6 @@ class UserRepository implements UserRepositoryInterface * This updates the users email address. Same as changeEmail just without most logging. This makes sure that the * undo/confirm routine can't catch this one. The user is NOT blocked. * - * * @see changeEmail */ public function updateEmail(User $user, string $newEmail): bool diff --git a/app/Repositories/UserGroup/UserGroupRepository.php b/app/Repositories/UserGroup/UserGroupRepository.php index 691807110c..80b3fc459c 100644 --- a/app/Repositories/UserGroup/UserGroupRepository.php +++ b/app/Repositories/UserGroup/UserGroupRepository.php @@ -96,7 +96,6 @@ class UserGroupRepository implements UserGroupRepositoryInterface, UserGroupInte /** * Returns all groups the user is member in. - * */ public function get(): Collection { @@ -165,7 +164,6 @@ class UserGroupRepository implements UserGroupRepositoryInterface, UserGroupInte /** * Returns all groups. - * */ public function getAll(): Collection { diff --git a/app/Rules/IsValidBulkClause.php b/app/Rules/IsValidBulkClause.php index f3c74630b1..df93b82a50 100644 --- a/app/Rules/IsValidBulkClause.php +++ b/app/Rules/IsValidBulkClause.php @@ -28,6 +28,7 @@ use Closure; use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Support\Facades\Validator; use JsonException; + use function Safe\json_decode; /** diff --git a/app/Rules/IsValidPositiveAmount.php b/app/Rules/IsValidPositiveAmount.php index f637cdbc6d..157307a3f8 100644 --- a/app/Rules/IsValidPositiveAmount.php +++ b/app/Rules/IsValidPositiveAmount.php @@ -29,6 +29,7 @@ use Closure; use FireflyIII\Support\Validation\ValidatesAmountsTrait; use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Support\Facades\Log; + use function Safe\json_encode; class IsValidPositiveAmount implements ValidationRule diff --git a/app/Rules/UniqueAccountNumber.php b/app/Rules/UniqueAccountNumber.php index d605010d9d..467c5e6c83 100644 --- a/app/Rules/UniqueAccountNumber.php +++ b/app/Rules/UniqueAccountNumber.php @@ -29,6 +29,7 @@ use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Models\Account; use FireflyIII\Models\AccountMeta; use Illuminate\Contracts\Validation\ValidationRule; + use function Safe\json_encode; /** diff --git a/app/Services/FireflyIIIOrg/Update/UpdateRequest.php b/app/Services/FireflyIIIOrg/Update/UpdateRequest.php index 99ead72517..ad55aee903 100644 --- a/app/Services/FireflyIIIOrg/Update/UpdateRequest.php +++ b/app/Services/FireflyIIIOrg/Update/UpdateRequest.php @@ -31,6 +31,7 @@ use GuzzleHttp\Client; use GuzzleHttp\Exception\GuzzleException; use Illuminate\Support\Facades\Log; use JsonException; + use function Safe\json_decode; /** diff --git a/app/Services/Internal/Support/AccountServiceTrait.php b/app/Services/Internal/Support/AccountServiceTrait.php index cf2f0e4dc6..15cb35f760 100644 --- a/app/Services/Internal/Support/AccountServiceTrait.php +++ b/app/Services/Internal/Support/AccountServiceTrait.php @@ -339,8 +339,6 @@ trait AccountServiceTrait return $this->accountRepository->getOpeningBalanceGroup($account); } - /** - */ protected function getCurrency(int $currencyId, string $currencyCode): TransactionCurrency { // find currency, or use default currency instead. diff --git a/app/Services/Internal/Support/JournalServiceTrait.php b/app/Services/Internal/Support/JournalServiceTrait.php index 757ce01c0d..a4ce044ebf 100644 --- a/app/Services/Internal/Support/JournalServiceTrait.php +++ b/app/Services/Internal/Support/JournalServiceTrait.php @@ -39,6 +39,7 @@ use FireflyIII\Rules\UniqueIban; use FireflyIII\Support\NullArrayObject; use Illuminate\Support\Facades\Log; use Safe\Exceptions\JsonException; + use function Safe\json_encode; /** @@ -265,11 +266,6 @@ trait JournalServiceTrait } /** - * @param Account|null $account - * @param array $data - * @param string $preferredType - * - * @return Account|null * @throws FireflyException * @throws JsonException */ diff --git a/app/Services/Internal/Support/RecurringTransactionTrait.php b/app/Services/Internal/Support/RecurringTransactionTrait.php index b6f5955cb2..ebb7aad99f 100644 --- a/app/Services/Internal/Support/RecurringTransactionTrait.php +++ b/app/Services/Internal/Support/RecurringTransactionTrait.php @@ -42,6 +42,7 @@ use FireflyIII\Models\RecurrenceTransactionMeta; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Validation\AccountValidator; use Illuminate\Support\Facades\Log; + use function Safe\json_encode; /** diff --git a/app/Services/Internal/Update/AccountUpdateService.php b/app/Services/Internal/Update/AccountUpdateService.php index 341e72b33a..9f38297e42 100644 --- a/app/Services/Internal/Update/AccountUpdateService.php +++ b/app/Services/Internal/Update/AccountUpdateService.php @@ -284,8 +284,6 @@ class AccountUpdateService } } - /** - */ private function updatePreferences(Account $account): void { $account->refresh(); diff --git a/app/Services/Internal/Update/BillUpdateService.php b/app/Services/Internal/Update/BillUpdateService.php index 4fdd94aae0..20825af2f2 100644 --- a/app/Services/Internal/Update/BillUpdateService.php +++ b/app/Services/Internal/Update/BillUpdateService.php @@ -45,8 +45,6 @@ class BillUpdateService protected User $user; - /** - */ public function update(Bill $bill, array $data): Bill { $this->user = $bill->user; diff --git a/app/Services/Webhook/StandardWebhookSender.php b/app/Services/Webhook/StandardWebhookSender.php index e0f6cd5dfb..ab9a82108a 100644 --- a/app/Services/Webhook/StandardWebhookSender.php +++ b/app/Services/Webhook/StandardWebhookSender.php @@ -34,6 +34,7 @@ use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Exception\RequestException; use Illuminate\Support\Facades\Log; use JsonException; + use function Safe\json_encode; /** diff --git a/app/Support/Binder/CLIToken.php b/app/Support/Binder/CLIToken.php index 6dc825c585..09c54de3f0 100644 --- a/app/Support/Binder/CLIToken.php +++ b/app/Support/Binder/CLIToken.php @@ -35,7 +35,6 @@ class CLIToken implements BinderInterface { /** * @return mixed - * */ public static function routeBinder(string $value, Route $route) { diff --git a/app/Support/CacheProperties.php b/app/Support/CacheProperties.php index 258bea8495..e22808ea06 100644 --- a/app/Support/CacheProperties.php +++ b/app/Support/CacheProperties.php @@ -27,6 +27,7 @@ use Carbon\Carbon; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Cache; use JsonException; + use function Safe\json_encode; /** diff --git a/app/Support/Export/ExportDataGenerator.php b/app/Support/Export/ExportDataGenerator.php index d1b19bf7e4..c9f25e12a5 100644 --- a/app/Support/Export/ExportDataGenerator.php +++ b/app/Support/Export/ExportDataGenerator.php @@ -107,7 +107,6 @@ class ExportDataGenerator } /** - * @return array * @throws CannotInsertRecord * @throws ContainerExceptionInterface * @throws Exception @@ -754,7 +753,6 @@ class ExportDataGenerator } /** - * @return string * @throws CannotInsertRecord * @throws Exception * @throws FireflyException diff --git a/app/Support/Http/Controllers/CreateStuff.php b/app/Support/Http/Controllers/CreateStuff.php index 32e0c2c1a4..a69c44ac54 100644 --- a/app/Support/Http/Controllers/CreateStuff.php +++ b/app/Support/Http/Controllers/CreateStuff.php @@ -32,6 +32,7 @@ use FireflyIII\User; use Illuminate\Support\Facades\Log; use Laravel\Passport\Passport; use phpseclib3\Crypt\RSA; + use function Safe\file_put_contents; /** diff --git a/app/Support/Http/Controllers/RequestInformation.php b/app/Support/Http/Controllers/RequestInformation.php index 4f9702bb29..39a6df3aff 100644 --- a/app/Support/Http/Controllers/RequestInformation.php +++ b/app/Support/Http/Controllers/RequestInformation.php @@ -35,6 +35,7 @@ use Illuminate\Routing\Route; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Route as RouteFacade; use Illuminate\Support\Facades\Validator; + use function Safe\parse_url; /** diff --git a/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php b/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php index 0df7aea164..e1a6be6a94 100644 --- a/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php +++ b/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php @@ -42,13 +42,13 @@ class BudgetLimitEnrichment implements EnrichmentInterface { private Collection $collection; private bool $convertToPrimary; // @phpstan-ignore-line - private array $currencies = []; - private array $currencyIds = []; + private array $currencies = []; + private array $currencyIds = []; private Carbon $end; - private array $expenses = []; - private array $ids = []; - private array $notes = []; - private array $pcExpenses = []; + private array $expenses = []; + private array $ids = []; + private array $notes = []; + private array $pcExpenses = []; private readonly TransactionCurrency $primaryCurrency; private Carbon $start; private User $user; diff --git a/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php b/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php index e51c110078..23f7b2d2d3 100644 --- a/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php +++ b/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php @@ -159,7 +159,7 @@ class PiggyBankEnrichment implements EnrichmentInterface // get suggested per month. $meta['save_per_month'] = Steam::bcround($this->getSuggestedMonthlyAmount($this->date, $item->target_date, $meta['target_amount'], $meta['current_amount']), $currency->decimal_places); - if(null !== $meta['pc_current_amount']) { + if (null !== $meta['pc_current_amount']) { $meta['pc_save_per_month'] = Steam::bcround($this->getSuggestedMonthlyAmount($this->date, $item->target_date, $meta['pc_target_amount'], $meta['pc_current_amount']), $currency->decimal_places); } diff --git a/app/Support/JsonApi/Enrichments/RecurringEnrichment.php b/app/Support/JsonApi/Enrichments/RecurringEnrichment.php index 25bb788c73..fb5115136a 100644 --- a/app/Support/JsonApi/Enrichments/RecurringEnrichment.php +++ b/app/Support/JsonApi/Enrichments/RecurringEnrichment.php @@ -51,6 +51,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; + use function Safe\json_decode; class RecurringEnrichment implements EnrichmentInterface @@ -60,7 +61,7 @@ class RecurringEnrichment implements EnrichmentInterface // private array $transactionTypeIds = []; // private array $transactionTypes = []; private bool $convertToPrimary; - private array $currencies = []; + private array $currencies = []; private array $currencyIds = []; private array $destinationAccountIds = []; private array $foreignCurrencyIds = []; diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php index 534eff6fe6..dd5981e327 100644 --- a/app/Support/Navigation.php +++ b/app/Support/Navigation.php @@ -413,20 +413,20 @@ class Navigation { $date = clone $theDate; $formatMap = [ - '1D' => (string)trans('config.specific_day_js'), - 'daily' => (string)trans('config.specific_day_js'), - 'custom' => (string)trans('config.specific_day_js'), - '1W' => (string)trans('config.week_in_year_js'), - 'week' => (string)trans('config.week_in_year_js'), - 'weekly' => (string)trans('config.week_in_year_js'), - '1M' => (string)trans('config.month_js'), - 'month' => (string)trans('config.month_js'), - 'monthly' => (string)trans('config.month_js'), - '1Y' => (string)trans('config.year_js'), + '1D' => (string)trans('config.specific_day_js'), + 'daily' => (string)trans('config.specific_day_js'), + 'custom' => (string)trans('config.specific_day_js'), + '1W' => (string)trans('config.week_in_year_js'), + 'week' => (string)trans('config.week_in_year_js'), + 'weekly' => (string)trans('config.week_in_year_js'), + '1M' => (string)trans('config.month_js'), + 'month' => (string)trans('config.month_js'), + 'monthly' => (string)trans('config.month_js'), + '1Y' => (string)trans('config.year_js'), 'YTD' => (string)trans('config.year_js'), - 'year' => (string)trans('config.year_js'), - 'yearly' => (string)trans('config.year_js'), - '6M' => (string)trans('config.half_year_js'), + 'year' => (string)trans('config.year_js'), + 'yearly' => (string)trans('config.year_js'), + '6M' => (string)trans('config.half_year_js'), ]; if (array_key_exists($repeatFrequency, $formatMap)) { diff --git a/app/Support/ParseDateString.php b/app/Support/ParseDateString.php index 883e76be41..46df80c290 100644 --- a/app/Support/ParseDateString.php +++ b/app/Support/ParseDateString.php @@ -30,6 +30,7 @@ use Carbon\Exceptions\InvalidFormatException; use FireflyIII\Exceptions\FireflyException; use Illuminate\Support\Facades\Log; use Safe\Exceptions\PcreException; + use function Safe\preg_match; /** @@ -72,11 +73,9 @@ class ParseDateString } /** - * @param string $date - * - * @return Carbon * @throws FireflyException * @throws PcreException + * * @SuppressWarnings("PHPMD.NPathComplexity") */ public function parseDate(string $date): Carbon diff --git a/app/Support/Preferences.php b/app/Support/Preferences.php index 5f00a32036..943d48b117 100644 --- a/app/Support/Preferences.php +++ b/app/Support/Preferences.php @@ -230,8 +230,6 @@ class Preferences return $this->getForUser($user, $name, $default); } - /** - */ public function lastActivity(): string { $instance = PreferencesSingleton::getInstance(); diff --git a/app/Support/Report/Budget/BudgetReportGenerator.php b/app/Support/Report/Budget/BudgetReportGenerator.php index ecdc897160..42f58a2728 100644 --- a/app/Support/Report/Budget/BudgetReportGenerator.php +++ b/app/Support/Report/Budget/BudgetReportGenerator.php @@ -131,8 +131,6 @@ class BudgetReportGenerator $this->start = $start; } - /** - */ public function setUser(User $user): void { $this->repository->setUser($user); diff --git a/app/Support/Request/ConvertsDataTypes.php b/app/Support/Request/ConvertsDataTypes.php index c978f2785c..5293a6f804 100644 --- a/app/Support/Request/ConvertsDataTypes.php +++ b/app/Support/Request/ConvertsDataTypes.php @@ -31,6 +31,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\Facades\Steam; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; + use function Safe\preg_replace; /** diff --git a/app/Support/Search/AccountSearch.php b/app/Support/Search/AccountSearch.php index 9e54a772d4..44bb34e893 100644 --- a/app/Support/Search/AccountSearch.php +++ b/app/Support/Search/AccountSearch.php @@ -28,6 +28,7 @@ use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Collection; + use function Safe\json_encode; /** diff --git a/app/Support/Search/OperatorQuerySearch.php b/app/Support/Search/OperatorQuerySearch.php index c9368d74af..5546fbc8c9 100644 --- a/app/Support/Search/OperatorQuerySearch.php +++ b/app/Support/Search/OperatorQuerySearch.php @@ -369,8 +369,6 @@ class OperatorQuerySearch implements SearchInterface } } - /** - */ private function parseDateRange(string $type, string $value): array { $parser = new ParseDateString(); diff --git a/app/Support/Search/QueryParser/GdbotsQueryParser.php b/app/Support/Search/QueryParser/GdbotsQueryParser.php index cc51218eaa..209b09721a 100644 --- a/app/Support/Search/QueryParser/GdbotsQueryParser.php +++ b/app/Support/Search/QueryParser/GdbotsQueryParser.php @@ -33,6 +33,7 @@ use Illuminate\Support\Facades\Log; use LogicException; use Safe\Exceptions\FilesystemException; use TypeError; + use function Safe\fwrite; class GdbotsQueryParser implements QueryParserInterface @@ -45,9 +46,6 @@ class GdbotsQueryParser implements QueryParserInterface } /** - * @param string $query - * - * @return NodeGroup * @throws FireflyException * @throws FilesystemException */ diff --git a/app/Support/Steam.php b/app/Support/Steam.php index f88839823d..10a8061153 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -40,6 +40,7 @@ use Illuminate\Support\Str; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use ValueError; + use function Safe\parse_url; use function Safe\preg_replace; @@ -534,8 +535,6 @@ class Steam return Amount::getTransactionCurrencyById((int)$result->data); } - /** - */ public function getHostName(string $ipAddress): string { $host = ''; @@ -557,7 +556,6 @@ class Steam /** * Get user's language. * - * @return string * @throws FireflyException * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface diff --git a/app/Support/System/OAuthKeys.php b/app/Support/System/OAuthKeys.php index e39ed5c5de..afb028180a 100644 --- a/app/Support/System/OAuthKeys.php +++ b/app/Support/System/OAuthKeys.php @@ -32,6 +32,7 @@ use Laravel\Passport\Console\KeysCommand; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Safe\Exceptions\FilesystemException; + use function Safe\file_get_contents; use function Safe\file_put_contents; @@ -79,7 +80,6 @@ class OAuthKeys } /** - * @return bool * @throws ContainerExceptionInterface * @throws FireflyException * @throws NotFoundExceptionInterface diff --git a/app/Support/Twig/General.php b/app/Support/Twig/General.php index a3b0a3e128..485dfe1bd9 100644 --- a/app/Support/Twig/General.php +++ b/app/Support/Twig/General.php @@ -37,6 +37,7 @@ use Override; use Twig\Extension\AbstractExtension; use Twig\TwigFilter; use Twig\TwigFunction; + use function Safe\parse_url; /** diff --git a/app/Support/Twig/TransactionGroupTwig.php b/app/Support/Twig/TransactionGroupTwig.php index 30b539f08a..3033a84872 100644 --- a/app/Support/Twig/TransactionGroupTwig.php +++ b/app/Support/Twig/TransactionGroupTwig.php @@ -34,6 +34,7 @@ use Illuminate\Support\Facades\DB; use Override; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; + use function Safe\json_decode; /** diff --git a/app/Transformers/PiggyBankTransformer.php b/app/Transformers/PiggyBankTransformer.php index a418b93d37..9088044890 100644 --- a/app/Transformers/PiggyBankTransformer.php +++ b/app/Transformers/PiggyBankTransformer.php @@ -45,7 +45,6 @@ class PiggyBankTransformer extends AbstractTransformer /** * Transform the piggy bank. - * */ public function transform(PiggyBank $piggyBank): array { diff --git a/app/Transformers/RecurrenceTransformer.php b/app/Transformers/RecurrenceTransformer.php index 823c4cecda..d2e1bb914c 100644 --- a/app/Transformers/RecurrenceTransformer.php +++ b/app/Transformers/RecurrenceTransformer.php @@ -39,7 +39,6 @@ class RecurrenceTransformer extends AbstractTransformer /** * Transform the recurring transaction. - * */ public function transform(Recurrence $recurrence): array { diff --git a/app/Transformers/UserTransformer.php b/app/Transformers/UserTransformer.php index 0dff6bd273..d4a06aefb0 100644 --- a/app/Transformers/UserTransformer.php +++ b/app/Transformers/UserTransformer.php @@ -36,7 +36,6 @@ class UserTransformer extends AbstractTransformer /** * Transform user. - * */ public function transform(User $user): array { diff --git a/app/Transformers/WebhookMessageTransformer.php b/app/Transformers/WebhookMessageTransformer.php index 8057d53a54..24bc25d4c2 100644 --- a/app/Transformers/WebhookMessageTransformer.php +++ b/app/Transformers/WebhookMessageTransformer.php @@ -27,6 +27,7 @@ namespace FireflyIII\Transformers; use FireflyIII\Models\WebhookMessage; use Illuminate\Support\Facades\Log; use JsonException; + use function Safe\json_encode; /** diff --git a/app/Validation/Api/Data/Bulk/ValidatesBulkTransactionQuery.php b/app/Validation/Api/Data/Bulk/ValidatesBulkTransactionQuery.php index 2ec2ee2fc9..8f8cffba91 100644 --- a/app/Validation/Api/Data/Bulk/ValidatesBulkTransactionQuery.php +++ b/app/Validation/Api/Data/Bulk/ValidatesBulkTransactionQuery.php @@ -26,6 +26,7 @@ namespace FireflyIII\Validation\Api\Data\Bulk; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use Illuminate\Validation\Validator; + use function Safe\json_decode; trait ValidatesBulkTransactionQuery diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index e13c866efc..e420a44f50 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -50,6 +50,7 @@ use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Safe\Exceptions\JsonException; use ValueError; + use function Safe\iconv; use function Safe\json_encode; use function Safe\preg_match; @@ -64,12 +65,12 @@ class FireflyValidator extends Validator * @param mixed $attribute * @param mixed $value * - * @return bool * @throws IncompatibleWithGoogleAuthenticatorException * @throws InvalidCharactersException * @throws SecretKeyTooShortException * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface + * * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ public function validate2faCode($attribute, $value): bool @@ -632,8 +633,8 @@ class FireflyValidator extends Validator * @param mixed $value * @param mixed $parameters * - * @return bool * @throws JsonException + * * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ public function validateUniqueAccountNumberForUser($attribute, $value, $parameters): bool diff --git a/config/firefly.php b/config/firefly.php index 906dab11e0..34bb614840 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -78,8 +78,8 @@ return [ 'running_balance_column' => env('USE_RUNNING_BALANCE', false), // see cer.php for exchange rates feature flag. ], - 'version' => 'develop/2025-10-03', - 'build_time' => 1759466687, + 'version' => 'develop/2025-10-05', + 'build_time' => 1759662132, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 28, // field is no longer used. diff --git a/package-lock.json b/package-lock.json index c97c70daf4..a38fd55cd2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2589,9 +2589,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.3.tgz", - "integrity": "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.4.tgz", + "integrity": "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==", "cpu": [ "arm" ], @@ -2603,9 +2603,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.3.tgz", - "integrity": "sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.4.tgz", + "integrity": "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==", "cpu": [ "arm64" ], @@ -2617,9 +2617,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.3.tgz", - "integrity": "sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.4.tgz", + "integrity": "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==", "cpu": [ "arm64" ], @@ -2631,9 +2631,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.3.tgz", - "integrity": "sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.4.tgz", + "integrity": "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==", "cpu": [ "x64" ], @@ -2645,9 +2645,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.3.tgz", - "integrity": "sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.4.tgz", + "integrity": "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==", "cpu": [ "arm64" ], @@ -2659,9 +2659,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.3.tgz", - "integrity": "sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.4.tgz", + "integrity": "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==", "cpu": [ "x64" ], @@ -2673,9 +2673,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.3.tgz", - "integrity": "sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.4.tgz", + "integrity": "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==", "cpu": [ "arm" ], @@ -2687,9 +2687,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.3.tgz", - "integrity": "sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.4.tgz", + "integrity": "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==", "cpu": [ "arm" ], @@ -2701,9 +2701,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.3.tgz", - "integrity": "sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.4.tgz", + "integrity": "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==", "cpu": [ "arm64" ], @@ -2715,9 +2715,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.3.tgz", - "integrity": "sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.4.tgz", + "integrity": "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==", "cpu": [ "arm64" ], @@ -2729,9 +2729,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.3.tgz", - "integrity": "sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.4.tgz", + "integrity": "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==", "cpu": [ "loong64" ], @@ -2743,9 +2743,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.3.tgz", - "integrity": "sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.4.tgz", + "integrity": "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==", "cpu": [ "ppc64" ], @@ -2757,9 +2757,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.3.tgz", - "integrity": "sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.4.tgz", + "integrity": "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==", "cpu": [ "riscv64" ], @@ -2771,9 +2771,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.3.tgz", - "integrity": "sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.4.tgz", + "integrity": "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==", "cpu": [ "riscv64" ], @@ -2785,9 +2785,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.3.tgz", - "integrity": "sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.4.tgz", + "integrity": "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==", "cpu": [ "s390x" ], @@ -2799,9 +2799,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.3.tgz", - "integrity": "sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.4.tgz", + "integrity": "sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==", "cpu": [ "x64" ], @@ -2813,9 +2813,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.3.tgz", - "integrity": "sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.4.tgz", + "integrity": "sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==", "cpu": [ "x64" ], @@ -2827,9 +2827,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.3.tgz", - "integrity": "sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.4.tgz", + "integrity": "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==", "cpu": [ "arm64" ], @@ -2841,9 +2841,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.3.tgz", - "integrity": "sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.4.tgz", + "integrity": "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==", "cpu": [ "arm64" ], @@ -2855,9 +2855,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.3.tgz", - "integrity": "sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.4.tgz", + "integrity": "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==", "cpu": [ "ia32" ], @@ -2869,9 +2869,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.3.tgz", - "integrity": "sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.4.tgz", + "integrity": "sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==", "cpu": [ "x64" ], @@ -2883,9 +2883,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.3.tgz", - "integrity": "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.4.tgz", + "integrity": "sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==", "cpu": [ "x64" ], @@ -3221,13 +3221,12 @@ "license": "MIT" }, "node_modules/@types/send": { - "version": "0.17.5", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", - "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.0.tgz", + "integrity": "sha512-zBF6vZJn1IaMpg3xUF25VK3gd3l8zwE0ZLRX7dsQyQi+jp4E8mMDJNGDYnYse+bQhYwWERTxVwHpi3dMOq7RKQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/mime": "^1", "@types/node": "*" } }, @@ -3242,15 +3241,26 @@ } }, "node_modules/@types/serve-static": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", - "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.9.tgz", + "integrity": "sha512-dOTIuqpWLyl3BBXU3maNQsS4A3zuuoYRNIvYSxxhebPfXg2mzWQEPne/nlJ37yOse6uGgR386uTpdsx4D0QZWA==", "dev": true, "license": "MIT", "dependencies": { "@types/http-errors": "*", "@types/node": "*", - "@types/send": "*" + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.5", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", + "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" } }, "node_modules/@types/sockjs": { @@ -4065,9 +4075,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.10", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.10.tgz", - "integrity": "sha512-uLfgBi+7IBNay8ECBO2mVMGZAc1VgZWEChxm4lv+TobGdG82LnXMjuNGo/BSSZZL4UmkWhxEHP2f5ziLNwGWMA==", + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.12.tgz", + "integrity": "sha512-vAPMQdnyKCBtkmQA6FMCBvU9qFIppS3nzyXnEM+Lo2IAhG4Mpjv9cCxMudhgV3YdNNJv6TNqXy97dfRVL2LmaQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4511,9 +4521,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001746", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001746.tgz", - "integrity": "sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA==", + "version": "1.0.30001747", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001747.tgz", + "integrity": "sha512-mzFa2DGIhuc5490Nd/G31xN1pnBnYMadtkyTjefPI7wzypqgCEpeWu9bJr0OnDsyKrW75zA9ZAt7pbQFmwLsQg==", "dev": true, "funding": [ { @@ -5726,9 +5736,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.229", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.229.tgz", - "integrity": "sha512-cwhDcZKGcT/rEthLRJ9eBlMDkh1sorgsuk+6dpsehV0g9CABsIqBxU4rLRjG+d/U6pYU1s37A4lSKrVc5lSQYg==", + "version": "1.5.230", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.230.tgz", + "integrity": "sha512-A6A6Fd3+gMdaed9wX83CvHYJb4UuapPD5X5SLq72VZJzxHSY0/LUweGXRWmQlh2ln7KV7iw7jnwXK7dlPoOnHQ==", "dev": true, "license": "ISC" }, @@ -5810,9 +5820,9 @@ } }, "node_modules/envinfo": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.15.0.tgz", - "integrity": "sha512-chR+t7exF6y59kelhXw5I3849nTy7KIRO+ePdLMhCD+JRP/JvmkenDWP7QSFGlsHX+kxGxdDutOPrmj5j1HR6g==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.16.0.tgz", + "integrity": "sha512-LqGbDxROFEWnYpUm9KiU2xHiYbjlHZS6NZQUpm1Lg1To4p+FJLks5crEhUnw+s/ZLHzI8U0QB+oVG7e8cXckMg==", "dev": true, "license": "MIT", "bin": { @@ -8454,9 +8464,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.21", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", - "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", + "version": "2.0.23", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.23.tgz", + "integrity": "sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==", "dev": true, "license": "MIT" }, @@ -10096,9 +10106,9 @@ } }, "node_modules/rollup": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.3.tgz", - "integrity": "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz", + "integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10112,28 +10122,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.52.3", - "@rollup/rollup-android-arm64": "4.52.3", - "@rollup/rollup-darwin-arm64": "4.52.3", - "@rollup/rollup-darwin-x64": "4.52.3", - "@rollup/rollup-freebsd-arm64": "4.52.3", - "@rollup/rollup-freebsd-x64": "4.52.3", - "@rollup/rollup-linux-arm-gnueabihf": "4.52.3", - "@rollup/rollup-linux-arm-musleabihf": "4.52.3", - "@rollup/rollup-linux-arm64-gnu": "4.52.3", - "@rollup/rollup-linux-arm64-musl": "4.52.3", - "@rollup/rollup-linux-loong64-gnu": "4.52.3", - "@rollup/rollup-linux-ppc64-gnu": "4.52.3", - "@rollup/rollup-linux-riscv64-gnu": "4.52.3", - "@rollup/rollup-linux-riscv64-musl": "4.52.3", - "@rollup/rollup-linux-s390x-gnu": "4.52.3", - "@rollup/rollup-linux-x64-gnu": "4.52.3", - "@rollup/rollup-linux-x64-musl": "4.52.3", - "@rollup/rollup-openharmony-arm64": "4.52.3", - "@rollup/rollup-win32-arm64-msvc": "4.52.3", - "@rollup/rollup-win32-ia32-msvc": "4.52.3", - "@rollup/rollup-win32-x64-gnu": "4.52.3", - "@rollup/rollup-win32-x64-msvc": "4.52.3", + "@rollup/rollup-android-arm-eabi": "4.52.4", + "@rollup/rollup-android-arm64": "4.52.4", + "@rollup/rollup-darwin-arm64": "4.52.4", + "@rollup/rollup-darwin-x64": "4.52.4", + "@rollup/rollup-freebsd-arm64": "4.52.4", + "@rollup/rollup-freebsd-x64": "4.52.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.4", + "@rollup/rollup-linux-arm-musleabihf": "4.52.4", + "@rollup/rollup-linux-arm64-gnu": "4.52.4", + "@rollup/rollup-linux-arm64-musl": "4.52.4", + "@rollup/rollup-linux-loong64-gnu": "4.52.4", + "@rollup/rollup-linux-ppc64-gnu": "4.52.4", + "@rollup/rollup-linux-riscv64-gnu": "4.52.4", + "@rollup/rollup-linux-riscv64-musl": "4.52.4", + "@rollup/rollup-linux-s390x-gnu": "4.52.4", + "@rollup/rollup-linux-x64-gnu": "4.52.4", + "@rollup/rollup-linux-x64-musl": "4.52.4", + "@rollup/rollup-openharmony-arm64": "4.52.4", + "@rollup/rollup-win32-arm64-msvc": "4.52.4", + "@rollup/rollup-win32-ia32-msvc": "4.52.4", + "@rollup/rollup-win32-x64-gnu": "4.52.4", + "@rollup/rollup-win32-x64-msvc": "4.52.4", "fsevents": "~2.3.2" } }, From 521db984abea3fe99599c8af9f2e8d6eb543091e Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 5 Oct 2025 13:09:42 +0200 Subject: [PATCH 83/91] More user friendly message. --- app/Console/Commands/Integrity/ReportsEmptyObjects.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Console/Commands/Integrity/ReportsEmptyObjects.php b/app/Console/Commands/Integrity/ReportsEmptyObjects.php index b2d26ba555..936d8cede3 100644 --- a/app/Console/Commands/Integrity/ReportsEmptyObjects.php +++ b/app/Console/Commands/Integrity/ReportsEmptyObjects.php @@ -70,7 +70,7 @@ class ReportsEmptyObjects extends Command /** @var stdClass $entry */ foreach ($set as $entry) { $line = sprintf( - 'User #%d (%s) has budget #%d ("%s") which has no transaction journals.', + 'User #%d (%s) has budget #%d ("%s") which has no transactions.', $entry->user_id, $entry->email, $entry->id, @@ -96,7 +96,7 @@ class ReportsEmptyObjects extends Command /** @var stdClass $entry */ foreach ($set as $entry) { $line = sprintf( - 'User #%d (%s) has category #%d ("%s") which has no transaction journals.', + 'User #%d (%s) has category #%d ("%s") which has no transactions.', $entry->user_id, $entry->email, $entry->id, @@ -119,7 +119,7 @@ class ReportsEmptyObjects extends Command /** @var stdClass $entry */ foreach ($set as $entry) { $line = sprintf( - 'User #%d (%s) has tag #%d ("%s") which has no transaction journals.', + 'User #%d (%s) has tag #%d ("%s") which has no transactions.', $entry->user_id, $entry->email, $entry->id, From c541cf48c37f3388428b6ccaeb7b074b849eb4a0 Mon Sep 17 00:00:00 2001 From: JC5 Date: Mon, 6 Oct 2025 05:21:33 +0200 Subject: [PATCH 84/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-10-06?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.lock | 12 ++++++------ config/firefly.php | 4 ++-- package-lock.json | 12 ++++++------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/composer.lock b/composer.lock index 982569fbbd..7199fbf526 100644 --- a/composer.lock +++ b/composer.lock @@ -4813,16 +4813,16 @@ }, { "name": "phpseclib/phpseclib", - "version": "3.0.46", + "version": "3.0.47", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6" + "reference": "9d6ca36a6c2dd434765b1071b2644a1c683b385d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6", - "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/9d6ca36a6c2dd434765b1071b2644a1c683b385d", + "reference": "9d6ca36a6c2dd434765b1071b2644a1c683b385d", "shasum": "" }, "require": { @@ -4903,7 +4903,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/3.0.46" + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.47" }, "funding": [ { @@ -4919,7 +4919,7 @@ "type": "tidelift" } ], - "time": "2025-06-26T16:29:55+00:00" + "time": "2025-10-06T01:07:24+00:00" }, { "name": "pragmarx/google2fa", diff --git a/config/firefly.php b/config/firefly.php index 34bb614840..733841fa2b 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -78,8 +78,8 @@ return [ 'running_balance_column' => env('USE_RUNNING_BALANCE', false), // see cer.php for exchange rates feature flag. ], - 'version' => 'develop/2025-10-05', - 'build_time' => 1759662132, + 'version' => 'develop/2025-10-06', + 'build_time' => 1759720765, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 28, // field is no longer used. diff --git a/package-lock.json b/package-lock.json index a38fd55cd2..7469f14c92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4521,9 +4521,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001747", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001747.tgz", - "integrity": "sha512-mzFa2DGIhuc5490Nd/G31xN1pnBnYMadtkyTjefPI7wzypqgCEpeWu9bJr0OnDsyKrW75zA9ZAt7pbQFmwLsQg==", + "version": "1.0.30001748", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001748.tgz", + "integrity": "sha512-5P5UgAr0+aBmNiplks08JLw+AW/XG/SurlgZLgB1dDLfAw7EfRGxIwzPHxdSCGY/BTKDqIVyJL87cCN6s0ZR0w==", "dev": true, "funding": [ { @@ -5820,9 +5820,9 @@ } }, "node_modules/envinfo": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.16.0.tgz", - "integrity": "sha512-LqGbDxROFEWnYpUm9KiU2xHiYbjlHZS6NZQUpm1Lg1To4p+FJLks5crEhUnw+s/ZLHzI8U0QB+oVG7e8cXckMg==", + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.16.1.tgz", + "integrity": "sha512-IugkinfKJlINIece0m1tAtjn4LGNP3APqchokVe090oSI5E3mixxKuc34ZiDHO17MTPODwjHWEt7QdC8Y+xtyQ==", "dev": true, "license": "MIT", "bin": { From 36e87f33839afef75f469678f736869958a3b859 Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 6 Oct 2025 20:43:47 +0200 Subject: [PATCH 85/91] Fix #11007 and fix #11005 --- app/Api/V1/Controllers/Autocomplete/AccountController.php | 2 +- app/Api/V1/Controllers/Models/Account/ShowController.php | 6 ++++++ app/Support/Request/ConvertsDataTypes.php | 3 ++- changelog.md | 2 ++ 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/Api/V1/Controllers/Autocomplete/AccountController.php b/app/Api/V1/Controllers/Autocomplete/AccountController.php index efd12f655e..c3f7cd9618 100644 --- a/app/Api/V1/Controllers/Autocomplete/AccountController.php +++ b/app/Api/V1/Controllers/Autocomplete/AccountController.php @@ -90,7 +90,7 @@ class AccountController extends Controller $timer->start(sprintf('AC accounts "%s"', $query)); $result = $this->repository->searchAccount((string) $query, $types, $this->parameters->get('limit')); - // set date to subday + end-of-day for account balance. so it is at $date 23:59:59 + // set date to end-of-day for account balance. so it is at $date 23:59:59 $date->endOfDay(); $allBalances = Steam::accountsBalancesOptimized($result, $date, $this->primaryCurrency, $this->convertToPrimary); diff --git a/app/Api/V1/Controllers/Models/Account/ShowController.php b/app/Api/V1/Controllers/Models/Account/ShowController.php index 230246bcf1..33c2e100d1 100644 --- a/app/Api/V1/Controllers/Models/Account/ShowController.php +++ b/app/Api/V1/Controllers/Models/Account/ShowController.php @@ -87,6 +87,9 @@ class ShowController extends Controller // TODO still need to figure out how to do this easily. $accounts = $collection->slice(($this->parameters->get('page') - 1) * $params['limit'], $params['limit']); + // #11007 go to the end of the previous day. + $this->parameters->set('start', $this->parameters->get('start')->subSecond()); + // enrich /** @var User $admin */ $admin = auth()->user(); @@ -125,6 +128,9 @@ class ShowController extends Controller $account->refresh(); $manager = $this->getManager(); + // #11007 go to the end of the previous day. + $this->parameters->set('start', $this->parameters->get('start')->subSecond()); + // enrich /** @var User $admin */ $admin = auth()->user(); diff --git a/app/Support/Request/ConvertsDataTypes.php b/app/Support/Request/ConvertsDataTypes.php index 5293a6f804..a57c45a320 100644 --- a/app/Support/Request/ConvertsDataTypes.php +++ b/app/Support/Request/ConvertsDataTypes.php @@ -314,7 +314,8 @@ trait ConvertsDataTypes // is an atom string, I hope? try { - $carbon = Carbon::parse($value, $value, config('app.timezone')); + $carbon = Carbon::parse($value); + $carbon->setTimezone(config('app.timezone')); } catch (InvalidDateException $e) { // @phpstan-ignore-line Log::error(sprintf('[3] "%s" is not a valid date or time: %s', $value, $e->getMessage())); diff --git a/changelog.md b/changelog.md index 588034223d..acd62c1168 100644 --- a/changelog.md +++ b/changelog.md @@ -29,6 +29,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). - [Discussion 10988](https://github.com/orgs/firefly-iii/discussions/10988) (Call to a member function startOfDay() on null.) started by @molnarti - [Issue 10990](https://github.com/firefly-iii/firefly-iii/issues/10990) (duplicate piggy event via API) reported by @4e868df3 - [Discussion 10994](https://github.com/orgs/firefly-iii/discussions/10994) (How does the save per month attribute from a piggy bank is calculated?) started by @AdriDevelopsThings +- #11005 +- #11007 ### API From def2dd77ab72b75f742493113a6b63825fe705c8 Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 6 Oct 2025 20:45:20 +0200 Subject: [PATCH 86/91] Fix #11003 --- routes/web.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/web.php b/routes/web.php index 4d86235d14..b37e68c5bc 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1427,7 +1427,7 @@ Route::group( static function (): void { Route::get('', ['uses' => 'UserGroup\IndexController@index', 'as' => 'index']); Route::get('create', ['uses' => 'UserGroup\CreateController@create', 'as' => 'create']); - Route::get('edit/{userGroup}', ['uses' => 'UserGroup\EditController@edit', 'as' => 'edit']); + Route::get('edit/{userGroup?}', ['uses' => 'UserGroup\EditController@edit', 'as' => 'edit']); // Route::get('show/{userGroup}', ['uses' => 'UserGroup\ShowController@show', 'as' => 'show']); // Route::post('rescan/{bill}', ['uses' => 'Bill\ShowController@rescan', 'as' => 'rescan']); From e6622754001e3c80757bb9f8af1dcd468003e524 Mon Sep 17 00:00:00 2001 From: Nicky De Maeyer Date: Mon, 6 Oct 2025 22:41:29 +0200 Subject: [PATCH 87/91] use the start and end date to enrich the accounts --- .../Controllers/Models/TransactionCurrency/ListController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php index 67a6486487..3467046482 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php @@ -108,6 +108,8 @@ class ListController extends Controller $admin = auth()->user(); $enrichment = new AccountEnrichment(); $enrichment->setDate($this->parameters->get('date')); + $enrichment->setStart($this->parameters->get('start')); + $enrichment->setEnd($this->parameters->get('end')); $enrichment->setUser($admin); $accounts = $enrichment->enrich($accounts); From c525a182635201963ea5deaee46c6d7f8a8e7bb7 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 7 Oct 2025 07:06:54 +0200 Subject: [PATCH 88/91] Add subSecond routine. --- .../Controllers/Models/TransactionCurrency/ListController.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php index f2c45161bc..a80200038d 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php @@ -100,6 +100,9 @@ class ListController extends Controller $count = $collection->count(); $accounts = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + // #11007 go to the end of the previous day. + $this->parameters->set('start', $this->parameters->get('start')->subSecond()); + // enrich /** @var User $admin */ $admin = auth()->user(); From 0070e000b613aad31cc6748351b3418dee833867 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 7 Oct 2025 07:09:12 +0200 Subject: [PATCH 89/91] Fix changelog. --- changelog.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index acd62c1168..ad6c6508a7 100644 --- a/changelog.md +++ b/changelog.md @@ -17,7 +17,6 @@ This project adheres to [Semantic Versioning](http://semver.org/). - [Discussion 10891](https://github.com/orgs/firefly-iii/discussions/10891) (User group id is null when downloading new exchange rates) started by @dakennguyen - [Discussion 10916](https://github.com/orgs/firefly-iii/discussions/10916) (Errors/Warnings in Logs after Batch API Import) started by @Mr-Kanister - [Issue 10920](https://github.com/firefly-iii/firefly-iii/issues/10920) (Liability transaction with same source and destination possible) reported by @Mr-Kanister -- [Issue 10921](https://github.com/firefly-iii/firefly-iii/issues/10921) (Transaction type between asset and liability not correctly enforced using API) reported by @Mr-Kanister - [Issue 10924](https://github.com/firefly-iii/firefly-iii/issues/10924) (Recurring transactions don't save (or show) selected subscription) reported by @SteffoSpieler - [Discussion 10938](https://github.com/orgs/firefly-iii/discussions/10938) (Unable to apply default rule group to certain transactions) started by @praemon - [Issue 10940](https://github.com/firefly-iii/firefly-iii/issues/10940) (Internal Server Error when trying to open piggy banks) reported by @mattephi @@ -27,15 +26,17 @@ This project adheres to [Semantic Versioning](http://semver.org/). - [Issue 10965](https://github.com/firefly-iii/firefly-iii/issues/10965) (Fix running balance on liability overview) reported by @JC5 - [Discussion 10974](https://github.com/orgs/firefly-iii/discussions/10974) (Big webhook_messages table) started by @Billos - [Discussion 10988](https://github.com/orgs/firefly-iii/discussions/10988) (Call to a member function startOfDay() on null.) started by @molnarti -- [Issue 10990](https://github.com/firefly-iii/firefly-iii/issues/10990) (duplicate piggy event via API) reported by @4e868df3 - [Discussion 10994](https://github.com/orgs/firefly-iii/discussions/10994) (How does the save per month attribute from a piggy bank is calculated?) started by @AdriDevelopsThings -- #11005 -- #11007 ### API - [Issue 10803](https://github.com/firefly-iii/firefly-iii/issues/10803) (Issue in /v1/budget-limits spent attribute) reported by @Billos - [Discussion 10908](https://github.com/orgs/firefly-iii/discussions/10908) (New fields for BudgetLimit object) started by @Billos +- [Issue 10921](https://github.com/firefly-iii/firefly-iii/issues/10921) (Transaction type between asset and liability not correctly enforced using API) reported by @Mr-Kanister +- [Issue 10990](https://github.com/firefly-iii/firefly-iii/issues/10990) (duplicate piggy event via API) reported by @4e868df3 +- #11005 +- #11007 +- #11010 ## 6.4.0 - 2025-09-14 From 0e1633b52b9a27f9269af8f879d439c176d4151b Mon Sep 17 00:00:00 2001 From: JC5 Date: Tue, 7 Oct 2025 07:13:03 +0200 Subject: [PATCH 90/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-10-07?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- changelog.md | 6 +++--- composer.lock | 14 +++++++------- config/firefly.php | 4 ++-- package-lock.json | 38 +++++++++++++++++++------------------- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/changelog.md b/changelog.md index ad6c6508a7..cb35c5e344 100644 --- a/changelog.md +++ b/changelog.md @@ -34,9 +34,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). - [Discussion 10908](https://github.com/orgs/firefly-iii/discussions/10908) (New fields for BudgetLimit object) started by @Billos - [Issue 10921](https://github.com/firefly-iii/firefly-iii/issues/10921) (Transaction type between asset and liability not correctly enforced using API) reported by @Mr-Kanister - [Issue 10990](https://github.com/firefly-iii/firefly-iii/issues/10990) (duplicate piggy event via API) reported by @4e868df3 -- #11005 -- #11007 -- #11010 +- [Issue 11005](https://github.com/firefly-iii/firefly-iii/issues/11005) (PUT /v1/accounts/{id} timezone error) reported by @cioraneanu +- [Issue 11007](https://github.com/firefly-iii/firefly-iii/issues/11007) (/v1/accounts balance_difference takes time into account, but api only accepts days) reported by @ctrl-f5 +- [Issue 11010](https://github.com/firefly-iii/firefly-iii/issues/11010) (/v1/currencies/{code}/accounts does not use start and end date for account enrichment) reported by @ctrl-f5 ## 6.4.0 - 2025-09-14 diff --git a/composer.lock b/composer.lock index 7199fbf526..2cdbc5e87e 100644 --- a/composer.lock +++ b/composer.lock @@ -11920,21 +11920,21 @@ }, { "name": "rector/rector", - "version": "2.1.7", + "version": "2.2.1", "source": { "type": "git", "url": "https://github.com/rectorphp/rector.git", - "reference": "c34cc07c4698f007a20dc5c99ff820089ae413ce" + "reference": "e1aaf3061e9ae9342ed0824865e3a3360defddeb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector/zipball/c34cc07c4698f007a20dc5c99ff820089ae413ce", - "reference": "c34cc07c4698f007a20dc5c99ff820089ae413ce", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/e1aaf3061e9ae9342ed0824865e3a3360defddeb", + "reference": "e1aaf3061e9ae9342ed0824865e3a3360defddeb", "shasum": "" }, "require": { "php": "^7.4|^8.0", - "phpstan/phpstan": "^2.1.18" + "phpstan/phpstan": "^2.1.26" }, "conflict": { "rector/rector-doctrine": "*", @@ -11968,7 +11968,7 @@ ], "support": { "issues": "https://github.com/rectorphp/rector/issues", - "source": "https://github.com/rectorphp/rector/tree/2.1.7" + "source": "https://github.com/rectorphp/rector/tree/2.2.1" }, "funding": [ { @@ -11976,7 +11976,7 @@ "type": "github" } ], - "time": "2025-09-10T11:13:58+00:00" + "time": "2025-10-06T21:25:14+00:00" }, { "name": "sebastian/cli-parser", diff --git a/config/firefly.php b/config/firefly.php index 733841fa2b..c0ce1dc9f8 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -78,8 +78,8 @@ return [ 'running_balance_column' => env('USE_RUNNING_BALANCE', false), // see cer.php for exchange rates feature flag. ], - 'version' => 'develop/2025-10-06', - 'build_time' => 1759720765, + 'version' => 'develop/2025-10-07', + 'build_time' => 1759813879, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 28, // field is no longer used. diff --git a/package-lock.json b/package-lock.json index 7469f14c92..9e5ed88c78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3047,9 +3047,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.7.tgz", - "integrity": "sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz", + "integrity": "sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==", "dev": true, "license": "MIT", "dependencies": { @@ -3060,9 +3060,9 @@ } }, "node_modules/@types/express/node_modules/@types/express-serve-static-core": { - "version": "4.19.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", - "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "version": "4.19.7", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz", + "integrity": "sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg==", "dev": true, "license": "MIT", "dependencies": { @@ -3173,13 +3173,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.6.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.2.tgz", - "integrity": "sha512-d2L25Y4j+W3ZlNAeMKcy7yDsK425ibcAOO2t7aPTz6gNMH0z2GThtwENCDc0d/Pw9wgyRqE5Px1wkV7naz8ang==", + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.0.tgz", + "integrity": "sha512-IbKooQVqUBrlzWTi79E8Fw78l8k1RNtlDDNWsFZs7XonuQSJ8oNYfEeclhprUldXISRMLzBpILuKgPlIxm+/Yw==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.13.0" + "undici-types": "~7.14.0" } }, "node_modules/@types/node-forge": { @@ -5736,9 +5736,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.230", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.230.tgz", - "integrity": "sha512-A6A6Fd3+gMdaed9wX83CvHYJb4UuapPD5X5SLq72VZJzxHSY0/LUweGXRWmQlh2ln7KV7iw7jnwXK7dlPoOnHQ==", + "version": "1.5.232", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.232.tgz", + "integrity": "sha512-ENirSe7wf8WzyPCibqKUG1Cg43cPaxH4wRR7AJsX7MCABCHBIOFqvaYODSLKUuZdraxUTHRE/0A2Aq8BYKEHOg==", "dev": true, "license": "ISC" }, @@ -5820,9 +5820,9 @@ } }, "node_modules/envinfo": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.16.1.tgz", - "integrity": "sha512-IugkinfKJlINIece0m1tAtjn4LGNP3APqchokVe090oSI5E3mixxKuc34ZiDHO17MTPODwjHWEt7QdC8Y+xtyQ==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.17.0.tgz", + "integrity": "sha512-GpfViocsFM7viwClFgxK26OtjMlKN67GCR5v6ASFkotxtpBWd9d+vNy+AH7F2E1TUkMDZ8P/dDPZX71/NG8xnQ==", "dev": true, "license": "MIT", "bin": { @@ -11316,9 +11316,9 @@ } }, "node_modules/undici-types": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.13.0.tgz", - "integrity": "sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", + "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==", "dev": true, "license": "MIT" }, From 9593f1b44e5313927e686d3bdd3fdfd23064ceaa Mon Sep 17 00:00:00 2001 From: JC5 Date: Tue, 7 Oct 2025 07:20:27 +0200 Subject: [PATCH 91/91] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'v6.4.1'=20on=202025-10-07?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/firefly.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/firefly.php b/config/firefly.php index c0ce1dc9f8..35f9953702 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -78,8 +78,8 @@ return [ 'running_balance_column' => env('USE_RUNNING_BALANCE', false), // see cer.php for exchange rates feature flag. ], - 'version' => 'develop/2025-10-07', - 'build_time' => 1759813879, + 'version' => '6.4.1', + 'build_time' => 1759814318, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 28, // field is no longer used.