diff --git a/.ci/php-cs-fixer/.php-cs-fixer.php b/.ci/php-cs-fixer/.php-cs-fixer.php index 6262b878f4..9ecec0fc77 100644 --- a/.ci/php-cs-fixer/.php-cs-fixer.php +++ b/.ci/php-cs-fixer/.php-cs-fixer.php @@ -61,7 +61,7 @@ return $config->setRules( 'comment_to_phpdoc' => false, // breaks phpstan lines in combination with PHPStorm. 'type_declaration_spaces' => false, 'cast_spaces' => false, - 'phpdoc_to_comment' => false, // do not overrule single line comment style, breaks phpstan. + 'phpdoc_to_comment' => false, // do not overrule single line comment style, breaks phpstan. // complex rules 'array_syntax' => ['syntax' => 'short'], diff --git a/.ci/rector.php b/.ci/rector.php index f758a43a09..ab39659e37 100644 --- a/.ci/rector.php +++ b/.ci/rector.php @@ -5,17 +5,17 @@ declare(strict_types=1); use Rector\Config\RectorConfig; return RectorConfig::configure() - ->withPaths([ - __DIR__ . '/../app', - __DIR__ . '/../bootstrap', - __DIR__ . '/../config', - __DIR__ . '/../public', - __DIR__ . '/../resources', - __DIR__ . '/../routes', - __DIR__ . '/../tests', - ]) + ->withPaths([ + __DIR__ . '/../app', + __DIR__ . '/../bootstrap', + __DIR__ . '/../config', + __DIR__ . '/../public', + __DIR__ . '/../resources', + __DIR__ . '/../routes', + __DIR__ . '/../tests', + ]) // uncomment to reach your current PHP version - ->withPhpSets() - ->withTypeCoverageLevel(0) - ->withDeadCodeLevel(0) - ->withCodeQualityLevel(0); + ->withPhpSets() + ->withTypeCoverageLevel(0) + ->withDeadCodeLevel(0) + ->withCodeQualityLevel(0); diff --git a/app/Api/V1/Controllers/Autocomplete/AccountController.php b/app/Api/V1/Controllers/Autocomplete/AccountController.php index 86b2a6f362..3cecdb08a0 100644 --- a/app/Api/V1/Controllers/Autocomplete/AccountController.php +++ b/app/Api/V1/Controllers/Autocomplete/AccountController.php @@ -43,6 +43,7 @@ use Illuminate\Support\Facades\Log; class AccountController extends Controller { use AccountFilter; + // this array only exists to test if the constructor will use it properly. protected array $accepts = ['application/json', 'application/vnd.api+json']; diff --git a/app/Api/V1/Controllers/Autocomplete/TransactionController.php b/app/Api/V1/Controllers/Autocomplete/TransactionController.php index 79b0687100..3cba382e52 100644 --- a/app/Api/V1/Controllers/Autocomplete/TransactionController.php +++ b/app/Api/V1/Controllers/Autocomplete/TransactionController.php @@ -39,11 +39,10 @@ use Illuminate\Support\Collection; */ class TransactionController extends Controller { + protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; private TransactionGroupRepositoryInterface $groupRepository; private JournalRepositoryInterface $repository; - protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; - /** * TransactionController constructor. */ diff --git a/app/Api/V1/Controllers/Chart/AccountController.php b/app/Api/V1/Controllers/Chart/AccountController.php index a7dbc10541..21150047c0 100644 --- a/app/Api/V1/Controllers/Chart/AccountController.php +++ b/app/Api/V1/Controllers/Chart/AccountController.php @@ -26,8 +26,8 @@ namespace FireflyIII\Api\V1\Controllers\Chart; use Carbon\Carbon; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Api\V1\Requests\Data\DateRequest; use FireflyIII\Api\V1\Requests\Chart\ChartRequest; +use FireflyIII\Api\V1\Requests\Data\DateRequest; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; @@ -48,8 +48,8 @@ class AccountController extends Controller use ApiSupport; use CollectsAccountsFromFilter; - private AccountRepositoryInterface $repository; private ChartData $chartData; + private AccountRepositoryInterface $repository; /** * AccountController constructor. @@ -93,6 +93,47 @@ class AccountController extends Controller return response()->json($this->chartData->render()); } + /** + * @throws FireflyException + */ + private function renderAccountData(array $params, Account $account): void + { + $currency = $this->repository->getAccountCurrency($account); + if (null === $currency) { + $currency = $this->default; + } + $currentSet = [ + 'label' => $account->name, + + // the currency that belongs to the account. + 'currency_id' => (string) $currency->id, + 'currency_code' => $currency->code, + 'currency_symbol' => $currency->symbol, + 'currency_decimal_places' => $currency->decimal_places, + + // the default currency of the user (could be the same!) + 'date' => $params['start']->toAtomString(), + 'start' => $params['start']->toAtomString(), + 'end' => $params['end']->toAtomString(), + 'period' => '1D', + 'entries' => [], + ]; + $currentStart = clone $params['start']; + $range = Steam::finalAccountBalanceInRange($account, $params['start'], clone $params['end'], $this->convertToNative); + + $previous = array_values($range)[0]['balance']; + while ($currentStart <= $params['end']) { + $format = $currentStart->format('Y-m-d'); + $label = $currentStart->toAtomString(); + $balance = array_key_exists($format, $range) ? $range[$format]['balance'] : $previous; + $previous = $balance; + + $currentStart->addDay(); + $currentSet['entries'][$label] = $balance; + } + $this->chartData->add($currentSet); + } + /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/charts/getChartAccountOverview @@ -162,45 +203,4 @@ class AccountController extends Controller return response()->json($chartData); } - - /** - * @throws FireflyException - */ - private function renderAccountData(array $params, Account $account): void - { - $currency = $this->repository->getAccountCurrency($account); - if (null === $currency) { - $currency = $this->default; - } - $currentSet = [ - 'label' => $account->name, - - // the currency that belongs to the account. - 'currency_id' => (string) $currency->id, - 'currency_code' => $currency->code, - 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => $currency->decimal_places, - - // the default currency of the user (could be the same!) - 'date' => $params['start']->toAtomString(), - 'start' => $params['start']->toAtomString(), - 'end' => $params['end']->toAtomString(), - 'period' => '1D', - 'entries' => [], - ]; - $currentStart = clone $params['start']; - $range = Steam::finalAccountBalanceInRange($account, $params['start'], clone $params['end'], $this->convertToNative); - - $previous = array_values($range)[0]['balance']; - while ($currentStart <= $params['end']) { - $format = $currentStart->format('Y-m-d'); - $label = $currentStart->toAtomString(); - $balance = array_key_exists($format, $range) ? $range[$format]['balance'] : $previous; - $previous = $balance; - - $currentStart->addDay(); - $currentSet['entries'][$label] = $balance; - } - $this->chartData->add($currentSet); - } } diff --git a/app/Api/V1/Controllers/Controller.php b/app/Api/V1/Controllers/Controller.php index a4b9bd297f..2566ddbe58 100644 --- a/app/Api/V1/Controllers/Controller.php +++ b/app/Api/V1/Controllers/Controller.php @@ -40,7 +40,6 @@ use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Routing\Controller as BaseController; -use Illuminate\Support\Collection; use League\Fractal\Manager; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; @@ -62,15 +61,15 @@ abstract class Controller extends BaseController use ValidatesRequests; use ValidatesUserGroupTrait; - protected const string CONTENT_TYPE = 'application/vnd.api+json'; - protected const string JSON_CONTENT_TYPE = 'application/json'; + protected const string CONTENT_TYPE = 'application/vnd.api+json'; + protected const string JSON_CONTENT_TYPE = 'application/json'; + protected array $accepts = ['application/json', 'application/vnd.api+json']; /** @var array */ - protected array $allowedSort; - protected ParameterBag $parameters; - protected bool $convertToNative = false; - protected array $accepts = ['application/json', 'application/vnd.api+json']; + protected array $allowedSort; + protected bool $convertToNative = false; protected TransactionCurrency $nativeCurrency; + protected ParameterBag $parameters; /** * Controller constructor. diff --git a/app/Api/V1/Controllers/Models/CurrencyExchangeRate/DestroyController.php b/app/Api/V1/Controllers/Models/CurrencyExchangeRate/DestroyController.php index ec6aa38ee9..9deedea808 100644 --- a/app/Api/V1/Controllers/Models/CurrencyExchangeRate/DestroyController.php +++ b/app/Api/V1/Controllers/Models/CurrencyExchangeRate/DestroyController.php @@ -39,10 +39,8 @@ class DestroyController extends Controller { use ValidatesUserGroupTrait; - protected array $acceptedRoles = [UserRoleEnum::OWNER]; - public const string RESOURCE_KEY = 'exchange-rates'; - + protected array $acceptedRoles = [UserRoleEnum::OWNER]; private ExchangeRateRepositoryInterface $repository; public function __construct() diff --git a/app/Api/V1/Controllers/Models/Transaction/StoreController.php b/app/Api/V1/Controllers/Models/Transaction/StoreController.php index 03cbd54dab..4491656f51 100644 --- a/app/Api/V1/Controllers/Models/Transaction/StoreController.php +++ b/app/Api/V1/Controllers/Models/Transaction/StoreController.php @@ -50,9 +50,8 @@ class StoreController extends Controller { use TransactionFilter; - private TransactionGroupRepositoryInterface $groupRepository; - protected array $acceptedRoles = [UserRoleEnum::MANAGE_TRANSACTIONS]; + private TransactionGroupRepositoryInterface $groupRepository; /** * TransactionController constructor. diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/DestroyController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/DestroyController.php index 2ce47e432e..7da589703f 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/DestroyController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/DestroyController.php @@ -27,8 +27,8 @@ namespace FireflyIII\Api\V1\Controllers\Models\TransactionCurrency; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; use Illuminate\Http\JsonResponse; diff --git a/app/Api/V1/Controllers/Summary/BasicController.php b/app/Api/V1/Controllers/Summary/BasicController.php index f974df4c1b..25d2346ea2 100644 --- a/app/Api/V1/Controllers/Summary/BasicController.php +++ b/app/Api/V1/Controllers/Summary/BasicController.php @@ -483,7 +483,7 @@ class BasicController extends Controller // first, create an entry for each entry in the "available" array. /** @var array $availableBudget */ - foreach ($available as $currencyId => $availableBudget) { + foreach ($available as $currencyId => $availableBudget) { $currencies[$currencyId] ??= $this->currencyRepos->find($currencyId); $return[$currencyId] = [ 'key' => sprintf('left-to-spend-in-%s', $currencies[$currencyId]->code), diff --git a/app/Api/V1/Requests/Models/CurrencyExchangeRate/DestroyRequest.php b/app/Api/V1/Requests/Models/CurrencyExchangeRate/DestroyRequest.php index eba660ab2b..451cf53126 100644 --- a/app/Api/V1/Requests/Models/CurrencyExchangeRate/DestroyRequest.php +++ b/app/Api/V1/Requests/Models/CurrencyExchangeRate/DestroyRequest.php @@ -45,7 +45,7 @@ class DestroyRequest extends FormRequest public function rules(): array { return [ - 'date' => 'required|date|after:1900-01-01|before:2099-12-31', + 'date' => 'required|date|after:1900-01-01|before:2099-12-31', ]; } } diff --git a/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreRequest.php b/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreRequest.php index 97b777518b..f29dd31fa2 100644 --- a/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreRequest.php +++ b/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreRequest.php @@ -40,16 +40,16 @@ class StoreRequest extends FormRequest return $this->getCarbonDate('date'); } - public function getRate(): string - { - return (string) $this->get('rate'); - } - public function getFromCurrency(): TransactionCurrency { return TransactionCurrency::where('code', $this->get('from'))->first(); } + public function getRate(): string + { + return (string) $this->get('rate'); + } + public function getToCurrency(): TransactionCurrency { return TransactionCurrency::where('code', $this->get('to'))->first(); diff --git a/app/Api/V1/Requests/Models/CurrencyExchangeRate/UpdateRequest.php b/app/Api/V1/Requests/Models/CurrencyExchangeRate/UpdateRequest.php index 14b9ff138c..e244895daa 100644 --- a/app/Api/V1/Requests/Models/CurrencyExchangeRate/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/CurrencyExchangeRate/UpdateRequest.php @@ -50,8 +50,8 @@ class UpdateRequest extends FormRequest public function rules(): array { return [ - 'date' => 'date|after:1900-01-01|before:2099-12-31', - 'rate' => 'required|numeric|gt:0', + 'date' => 'date|after:1900-01-01|before:2099-12-31', + 'rate' => 'required|numeric|gt:0', ]; } } diff --git a/app/Api/V1/Requests/Models/Transaction/StoreRequest.php b/app/Api/V1/Requests/Models/Transaction/StoreRequest.php index e651e5a1d6..c9a99f88eb 100644 --- a/app/Api/V1/Requests/Models/Transaction/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Transaction/StoreRequest.php @@ -83,87 +83,87 @@ class StoreRequest extends FormRequest foreach ($this->get('transactions') as $transaction) { $object = new NullArrayObject($transaction); $return[] = [ - 'type' => $this->clearString($object['type']), - 'date' => $this->dateFromValue($object['date']), - 'order' => $this->integerFromValue((string) $object['order']), + 'type' => $this->clearString($object['type']), + 'date' => $this->dateFromValue($object['date']), + 'order' => $this->integerFromValue((string) $object['order']), - 'currency_id' => $this->integerFromValue((string) $object['currency_id']), - 'currency_code' => $this->clearString((string) $object['currency_code']), + 'currency_id' => $this->integerFromValue((string) $object['currency_id']), + 'currency_code' => $this->clearString((string) $object['currency_code']), // location - 'latitude' => $this->floatFromValue((string) $object['latitude']), - 'longitude' => $this->floatFromValue((string) $object['longitude']), - 'zoom_level' => $this->integerFromValue((string) $object['zoom_level']), + 'latitude' => $this->floatFromValue((string) $object['latitude']), + 'longitude' => $this->floatFromValue((string) $object['longitude']), + 'zoom_level' => $this->integerFromValue((string) $object['zoom_level']), // foreign currency info: - 'foreign_currency_id' => $this->integerFromValue((string) $object['foreign_currency_id']), - 'foreign_currency_code' => $this->clearString((string) $object['foreign_currency_code']), + 'foreign_currency_id' => $this->integerFromValue((string) $object['foreign_currency_id']), + 'foreign_currency_code' => $this->clearString((string) $object['foreign_currency_code']), // amount and foreign amount. Cannot be 0. - 'amount' => $this->clearString((string) $object['amount']), - 'foreign_amount' => $this->clearString((string) $object['foreign_amount']), + 'amount' => $this->clearString((string) $object['amount']), + 'foreign_amount' => $this->clearString((string) $object['foreign_amount']), // description. - 'description' => $this->clearString($object['description']), + 'description' => $this->clearString($object['description']), // source of transaction. If everything is null, assume cash account. - 'source_id' => $this->integerFromValue((string) $object['source_id']), - 'source_name' => $this->clearString((string) $object['source_name']), - 'source_iban' => $this->clearIban((string) $object['source_iban']), - 'source_number' => $this->clearString((string) $object['source_number']), - 'source_bic' => $this->clearString((string) $object['source_bic']), + 'source_id' => $this->integerFromValue((string) $object['source_id']), + 'source_name' => $this->clearString((string) $object['source_name']), + 'source_iban' => $this->clearIban((string) $object['source_iban']), + 'source_number' => $this->clearString((string) $object['source_number']), + 'source_bic' => $this->clearString((string) $object['source_bic']), // destination of transaction. If everything is null, assume cash account. - 'destination_id' => $this->integerFromValue((string) $object['destination_id']), - 'destination_name' => $this->clearString((string) $object['destination_name']), - 'destination_iban' => $this->clearIban((string) $object['destination_iban']), - 'destination_number' => $this->clearString((string) $object['destination_number']), - 'destination_bic' => $this->clearString((string) $object['destination_bic']), + 'destination_id' => $this->integerFromValue((string) $object['destination_id']), + 'destination_name' => $this->clearString((string) $object['destination_name']), + 'destination_iban' => $this->clearIban((string) $object['destination_iban']), + 'destination_number' => $this->clearString((string) $object['destination_number']), + 'destination_bic' => $this->clearString((string) $object['destination_bic']), // budget info - 'budget_id' => $this->integerFromValue((string) $object['budget_id']), - 'budget_name' => $this->clearString((string) $object['budget_name']), + 'budget_id' => $this->integerFromValue((string) $object['budget_id']), + 'budget_name' => $this->clearString((string) $object['budget_name']), // category info - 'category_id' => $this->integerFromValue((string) $object['category_id']), - 'category_name' => $this->clearString((string) $object['category_name']), + 'category_id' => $this->integerFromValue((string) $object['category_id']), + 'category_name' => $this->clearString((string) $object['category_name']), // journal bill reference. Optional. Will only work for withdrawals - 'bill_id' => $this->integerFromValue((string) $object['bill_id']), - 'bill_name' => $this->clearString((string) $object['bill_name']), + 'bill_id' => $this->integerFromValue((string) $object['bill_id']), + 'bill_name' => $this->clearString((string) $object['bill_name']), // piggy bank reference. Optional. Will only work for transfers - 'piggy_bank_id' => $this->integerFromValue((string) $object['piggy_bank_id']), - 'piggy_bank_name' => $this->clearString((string) $object['piggy_bank_name']), + 'piggy_bank_id' => $this->integerFromValue((string) $object['piggy_bank_id']), + 'piggy_bank_name' => $this->clearString((string) $object['piggy_bank_name']), // some other interesting properties - 'reconciled' => $this->convertBoolean((string) $object['reconciled']), - 'notes' => $this->clearStringKeepNewlines((string) $object['notes']), - 'tags' => $this->arrayFromValue($object['tags']), + 'reconciled' => $this->convertBoolean((string) $object['reconciled']), + 'notes' => $this->clearStringKeepNewlines((string) $object['notes']), + 'tags' => $this->arrayFromValue($object['tags']), // all custom fields: - 'internal_reference' => $this->clearString((string) $object['internal_reference']), - 'external_id' => $this->clearString((string) $object['external_id']), - 'original_source' => sprintf('ff3-v%s', config('firefly.version')), - 'recurrence_id' => $this->integerFromValue($object['recurrence_id']), - 'bunq_payment_id' => $this->clearString((string) $object['bunq_payment_id']), - 'external_url' => $this->clearString((string) $object['external_url']), + 'internal_reference' => $this->clearString((string) $object['internal_reference']), + 'external_id' => $this->clearString((string) $object['external_id']), + 'original_source' => sprintf('ff3-v%s', config('firefly.version')), + 'recurrence_id' => $this->integerFromValue($object['recurrence_id']), + 'bunq_payment_id' => $this->clearString((string) $object['bunq_payment_id']), + 'external_url' => $this->clearString((string) $object['external_url']), - 'sepa_cc' => $this->clearString((string) $object['sepa_cc']), - 'sepa_ct_op' => $this->clearString((string) $object['sepa_ct_op']), - 'sepa_ct_id' => $this->clearString((string) $object['sepa_ct_id']), - 'sepa_db' => $this->clearString((string) $object['sepa_db']), - 'sepa_country' => $this->clearString((string) $object['sepa_country']), - 'sepa_ep' => $this->clearString((string) $object['sepa_ep']), - 'sepa_ci' => $this->clearString((string) $object['sepa_ci']), - 'sepa_batch_id' => $this->clearString((string) $object['sepa_batch_id']), + 'sepa_cc' => $this->clearString((string) $object['sepa_cc']), + 'sepa_ct_op' => $this->clearString((string) $object['sepa_ct_op']), + 'sepa_ct_id' => $this->clearString((string) $object['sepa_ct_id']), + 'sepa_db' => $this->clearString((string) $object['sepa_db']), + 'sepa_country' => $this->clearString((string) $object['sepa_country']), + 'sepa_ep' => $this->clearString((string) $object['sepa_ep']), + 'sepa_ci' => $this->clearString((string) $object['sepa_ci']), + 'sepa_batch_id' => $this->clearString((string) $object['sepa_batch_id']), // custom date fields. Must be Carbon objects. Presence is optional. - 'interest_date' => $this->dateFromValue($object['interest_date']), - 'book_date' => $this->dateFromValue($object['book_date']), - 'process_date' => $this->dateFromValue($object['process_date']), - 'due_date' => $this->dateFromValue($object['due_date']), - 'payment_date' => $this->dateFromValue($object['payment_date']), - 'invoice_date' => $this->dateFromValue($object['invoice_date']), + 'interest_date' => $this->dateFromValue($object['interest_date']), + 'book_date' => $this->dateFromValue($object['book_date']), + 'process_date' => $this->dateFromValue($object['process_date']), + 'due_date' => $this->dateFromValue($object['due_date']), + 'payment_date' => $this->dateFromValue($object['payment_date']), + 'invoice_date' => $this->dateFromValue($object['invoice_date']), ]; } diff --git a/app/Api/V2/Controllers/Controller.php b/app/Api/V2/Controllers/Controller.php index 5431e1af50..d3c5207fe2 100644 --- a/app/Api/V2/Controllers/Controller.php +++ b/app/Api/V2/Controllers/Controller.php @@ -33,7 +33,6 @@ use FireflyIII\Transformers\AbstractTransformer; use Illuminate\Database\Eloquent\Model; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Routing\Controller as BaseController; -use Illuminate\Support\Collection; use League\Fractal\Manager; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; @@ -56,8 +55,8 @@ class Controller extends BaseController protected const string CONTENT_TYPE = 'application/vnd.api+json'; protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; - protected ParameterBag $parameters; protected bool $convertToNative = false; + protected ParameterBag $parameters; public function __construct() { diff --git a/app/Api/V2/Controllers/Transaction/List/TransactionController.php b/app/Api/V2/Controllers/Transaction/List/TransactionController.php index 0e630d7444..0de0879812 100644 --- a/app/Api/V2/Controllers/Transaction/List/TransactionController.php +++ b/app/Api/V2/Controllers/Transaction/List/TransactionController.php @@ -114,7 +114,6 @@ class TransactionController extends Controller return response() ->json($this->jsonApiList('transactions', $paginator, new TransactionGroupTransformer())) - ->header('Content-Type', self::CONTENT_TYPE) - ; + ->header('Content-Type', self::CONTENT_TYPE); } } diff --git a/app/Console/Commands/Correction/CorrectsAmounts.php b/app/Console/Commands/Correction/CorrectsAmounts.php index 3dc797dbb7..fdc206cf80 100644 --- a/app/Console/Commands/Correction/CorrectsAmounts.php +++ b/app/Console/Commands/Correction/CorrectsAmounts.php @@ -75,6 +75,65 @@ class CorrectsAmounts extends Command return 0; } + private function correctTransfers(): void + { + /** @var AccountRepositoryInterface $repository */ + $repository = app(AccountRepositoryInterface::class); + $type = TransactionType::where('type', TransactionTypeEnum::TRANSFER->value)->first(); + $journals = TransactionJournal::where('transaction_type_id', $type->id)->get(); + + /** @var TransactionJournal $journal */ + foreach ($journals as $journal) { + $repository->setUser($journal->user); + $native = Amount::getNativeCurrencyByUserGroup($journal->userGroup); + + /** @var null|Transaction $source */ + $source = $journal->transactions()->where('amount', '<', 0)->first(); + + /** @var null|Transaction $destination */ + $destination = $journal->transactions()->where('amount', '>', 0)->first(); + if (null === $source || null === $destination) { + continue; + } + if (null === $source->foreign_currency_id || null === $destination->foreign_currency_id) { + continue; + } + $sourceAccount = $source->account; + $destAccount = $destination->account; + if (null === $sourceAccount || null === $destAccount) { + continue; + } + $sourceCurrency = $repository->getAccountCurrency($sourceAccount) ?? $native; + $destCurrency = $repository->getAccountCurrency($destAccount) ?? $native; + + if ($sourceCurrency->id === $destCurrency->id) { + Log::debug('Both accounts have the same currency. Removing foreign currency info.'); + $source->foreign_currency_id = null; + $source->foreign_amount = null; + $source->save(); + $destination->foreign_currency_id = null; + $destination->foreign_amount = null; + $destination->save(); + + continue; + } + + // validate source + if ($destCurrency->id !== $source->foreign_currency_id) { + Log::debug(sprintf('Journal #%d: Transaction #%d refers to "%s" but should refer to "%s".', $journal->id, $source->id, $source->foreignCurrency->code, $destCurrency->code)); + $source->foreign_currency_id = $destCurrency->id; + $source->save(); + } + + // validate destination: + if ($sourceCurrency->id !== $destination->foreign_currency_id) { + Log::debug(sprintf('Journal #%d: Transaction #%d refers to "%s" but should refer to "%s".', $journal->id, $destination->id, $destination->foreignCurrency->code, $sourceCurrency->code)); + $destination->foreign_currency_id = $sourceCurrency->id; + $destination->save(); + } + } + } + private function fixAutoBudgets(): void { $count = AutoBudget::where('amount', '<', 0)->update(['amount' => DB::raw('amount * -1')]); @@ -192,63 +251,4 @@ class CorrectsAmounts extends Command return false; } - - private function correctTransfers(): void - { - /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class); - $type = TransactionType::where('type', TransactionTypeEnum::TRANSFER->value)->first(); - $journals = TransactionJournal::where('transaction_type_id', $type->id)->get(); - - /** @var TransactionJournal $journal */ - foreach ($journals as $journal) { - $repository->setUser($journal->user); - $native = Amount::getNativeCurrencyByUserGroup($journal->userGroup); - - /** @var null|Transaction $source */ - $source = $journal->transactions()->where('amount', '<', 0)->first(); - - /** @var null|Transaction $destination */ - $destination = $journal->transactions()->where('amount', '>', 0)->first(); - if (null === $source || null === $destination) { - continue; - } - if (null === $source->foreign_currency_id || null === $destination->foreign_currency_id) { - continue; - } - $sourceAccount = $source->account; - $destAccount = $destination->account; - if (null === $sourceAccount || null === $destAccount) { - continue; - } - $sourceCurrency = $repository->getAccountCurrency($sourceAccount) ?? $native; - $destCurrency = $repository->getAccountCurrency($destAccount) ?? $native; - - if ($sourceCurrency->id === $destCurrency->id) { - Log::debug('Both accounts have the same currency. Removing foreign currency info.'); - $source->foreign_currency_id = null; - $source->foreign_amount = null; - $source->save(); - $destination->foreign_currency_id = null; - $destination->foreign_amount = null; - $destination->save(); - - continue; - } - - // validate source - if ($destCurrency->id !== $source->foreign_currency_id) { - Log::debug(sprintf('Journal #%d: Transaction #%d refers to "%s" but should refer to "%s".', $journal->id, $source->id, $source->foreignCurrency->code, $destCurrency->code)); - $source->foreign_currency_id = $destCurrency->id; - $source->save(); - } - - // validate destination: - if ($sourceCurrency->id !== $destination->foreign_currency_id) { - Log::debug(sprintf('Journal #%d: Transaction #%d refers to "%s" but should refer to "%s".', $journal->id, $destination->id, $destination->foreignCurrency->code, $sourceCurrency->code)); - $destination->foreign_currency_id = $sourceCurrency->id; - $destination->save(); - } - } - } } diff --git a/app/Console/Commands/Correction/CorrectsNativeAmounts.php b/app/Console/Commands/Correction/CorrectsNativeAmounts.php index 6e6935c049..87d84abe66 100644 --- a/app/Console/Commands/Correction/CorrectsNativeAmounts.php +++ b/app/Console/Commands/Correction/CorrectsNativeAmounts.php @@ -38,8 +38,8 @@ use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\UserGroup; -use FireflyIII\Repositories\UserGroup\UserGroupRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; +use FireflyIII\Repositories\UserGroup\UserGroupRepositoryInterface; use FireflyIII\Support\Facades\Preferences; use FireflyIII\Support\Http\Api\ExchangeRateConverter; use Illuminate\Console\Command; diff --git a/app/Console/Commands/Correction/RemovesEmptyGroups.php b/app/Console/Commands/Correction/RemovesEmptyGroups.php index 7f64fc048a..fd07c3cbb9 100644 --- a/app/Console/Commands/Correction/RemovesEmptyGroups.php +++ b/app/Console/Commands/Correction/RemovesEmptyGroups.php @@ -44,7 +44,7 @@ class RemovesEmptyGroups extends Command { $groupIds = TransactionGroup::leftJoin('transaction_journals', 'transaction_groups.id', '=', 'transaction_journals.transaction_group_id') - ->whereNull('transaction_journals.id')->get(['transaction_groups.id'])->pluck('id')->toArray() + ->whereNull('transaction_journals.id')->get(['transaction_groups.id'])->pluck('id')->toArray() ; $total = count($groupIds); diff --git a/app/Console/Commands/Integrity/ValidatesEnvironmentVariables.php b/app/Console/Commands/Integrity/ValidatesEnvironmentVariables.php index 1848770afe..aceaa211be 100644 --- a/app/Console/Commands/Integrity/ValidatesEnvironmentVariables.php +++ b/app/Console/Commands/Integrity/ValidatesEnvironmentVariables.php @@ -30,13 +30,6 @@ class ValidatesEnvironmentVariables extends Command { use ShowsFriendlyMessages; - /** - * The name and signature of the console command. - * - * @var string - */ - protected $signature = 'integrity:validates-environment-variables'; - /** * The console command description. * @@ -44,6 +37,13 @@ class ValidatesEnvironmentVariables extends Command */ protected $description = 'Makes sure you use the correct variables.'; + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'integrity:validates-environment-variables'; + /** * Execute the console command. */ diff --git a/app/Console/Commands/System/OutputsInstructions.php b/app/Console/Commands/System/OutputsInstructions.php index ae703f8399..9dd17f2fff 100644 --- a/app/Console/Commands/System/OutputsInstructions.php +++ b/app/Console/Commands/System/OutputsInstructions.php @@ -172,6 +172,13 @@ class OutputsInstructions extends Command } } + private function donationText(): void + { + $this->boxed('Did you know you can support the development of Firefly III?'); + $this->boxed('You can donate in many ways, like GitHub Sponsors or Patreon.'); + $this->boxed('For more information, please visit https://bit.ly/donate-to-Firefly-III'); + } + /** * Render instructions. */ @@ -225,11 +232,4 @@ class OutputsInstructions extends Command $this->boxed(''); $this->showLine(); } - - private function donationText(): void - { - $this->boxed('Did you know you can support the development of Firefly III?'); - $this->boxed('You can donate in many ways, like GitHub Sponsors or Patreon.'); - $this->boxed('For more information, please visit https://bit.ly/donate-to-Firefly-III'); - } } diff --git a/app/Console/Commands/System/RecalculatesRunningBalance.php b/app/Console/Commands/System/RecalculatesRunningBalance.php index cd0c3874b4..5e51a4fcc6 100644 --- a/app/Console/Commands/System/RecalculatesRunningBalance.php +++ b/app/Console/Commands/System/RecalculatesRunningBalance.php @@ -31,13 +31,6 @@ class RecalculatesRunningBalance extends Command { use ShowsFriendlyMessages; - /** - * The name and signature of the console command. - * - * @var string - */ - protected $signature = 'firefly-iii:refresh-running-balance {--F|force : Force the execution of this command.}'; - /** * The console command description. * @@ -45,6 +38,13 @@ class RecalculatesRunningBalance extends Command */ protected $description = 'Refreshes all running balances. May take a long time to run if forced.'; + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'firefly-iii:refresh-running-balance {--F|force : Force the execution of this command.}'; + /** * Execute the console command. */ diff --git a/app/Console/Commands/Tools/Cron.php b/app/Console/Commands/Tools/Cron.php index b7176894c0..c220ccf307 100644 --- a/app/Console/Commands/Tools/Cron.php +++ b/app/Console/Commands/Tools/Cron.php @@ -150,6 +150,23 @@ class Cron extends Command } } + private function checkForUpdates(bool $force): void + { + $updateCheck = new UpdateCheckCronjob(); + $updateCheck->setForce($force); + $updateCheck->fire(); + + if ($updateCheck->jobErrored) { + $this->friendlyError(sprintf('Error in "update check" cron: %s', $updateCheck->message)); + } + if ($updateCheck->jobFired) { + $this->friendlyInfo(sprintf('"Update check" cron fired: %s', $updateCheck->message)); + } + if ($updateCheck->jobSucceeded) { + $this->friendlyPositive(sprintf('"Update check" cron ran with success: %s', $updateCheck->message)); + } + } + /** * @throws FireflyException */ @@ -221,21 +238,4 @@ class Cron extends Command $this->friendlyPositive(sprintf('"Send bill warnings" cron ran with success: %s', $autoBudget->message)); } } - - private function checkForUpdates(bool $force): void - { - $updateCheck = new UpdateCheckCronjob(); - $updateCheck->setForce($force); - $updateCheck->fire(); - - if ($updateCheck->jobErrored) { - $this->friendlyError(sprintf('Error in "update check" cron: %s', $updateCheck->message)); - } - if ($updateCheck->jobFired) { - $this->friendlyInfo(sprintf('"Update check" cron fired: %s', $updateCheck->message)); - } - if ($updateCheck->jobSucceeded) { - $this->friendlyPositive(sprintf('"Update check" cron ran with success: %s', $updateCheck->message)); - } - } } diff --git a/app/Console/Commands/Upgrade/UpgradesNativeAmounts.php b/app/Console/Commands/Upgrade/UpgradesNativeAmounts.php index d04895c342..648237b33c 100644 --- a/app/Console/Commands/Upgrade/UpgradesNativeAmounts.php +++ b/app/Console/Commands/Upgrade/UpgradesNativeAmounts.php @@ -32,6 +32,7 @@ use Illuminate\Support\Facades\Artisan; class UpgradesNativeAmounts extends Command { use ShowsFriendlyMessages; + public const string CONFIG_NAME = '620_native_amounts'; protected $description = 'Runs the native amounts calculations.'; @@ -61,7 +62,7 @@ class UpgradesNativeAmounts extends Command { $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/Events/DestroyedTransactionLink.php b/app/Events/DestroyedTransactionLink.php index e0d128bc6e..60d43011ad 100644 --- a/app/Events/DestroyedTransactionLink.php +++ b/app/Events/DestroyedTransactionLink.php @@ -32,7 +32,9 @@ use Illuminate\Queue\SerializesModels; */ class DestroyedTransactionLink extends Event { - use SerializesModels; // @phpstan-ignore-line + use SerializesModels; + + // @phpstan-ignore-line /** * DestroyedTransactionLink constructor. diff --git a/app/Events/Model/PiggyBank/ChangedAmount.php b/app/Events/Model/PiggyBank/ChangedAmount.php index d08f93e584..f60e8a5113 100644 --- a/app/Events/Model/PiggyBank/ChangedAmount.php +++ b/app/Events/Model/PiggyBank/ChangedAmount.php @@ -37,8 +37,8 @@ class ChangedAmount extends Event { use SerializesModels; - public string $amount; - public PiggyBank $piggyBank; + public string $amount; + public PiggyBank $piggyBank; /** * Create a new event instance. diff --git a/app/Events/Security/MFABackupFewLeft.php b/app/Events/Security/MFABackupFewLeft.php index 141c6f1e69..0379afeb3e 100644 --- a/app/Events/Security/MFABackupFewLeft.php +++ b/app/Events/Security/MFABackupFewLeft.php @@ -32,6 +32,7 @@ use Illuminate\Queue\SerializesModels; class MFABackupFewLeft extends Event { use SerializesModels; + public User $user; public function __construct(null|Authenticatable|User $user, public int $count) diff --git a/app/Events/Security/MFAManyFailedAttempts.php b/app/Events/Security/MFAManyFailedAttempts.php index 0bbaf6ea08..e1b9067c5b 100644 --- a/app/Events/Security/MFAManyFailedAttempts.php +++ b/app/Events/Security/MFAManyFailedAttempts.php @@ -32,6 +32,7 @@ use Illuminate\Queue\SerializesModels; class MFAManyFailedAttempts extends Event { use SerializesModels; + public User $user; public function __construct(null|Authenticatable|User $user, public int $count) diff --git a/app/Events/Test/OwnerTestNotificationChannel.php b/app/Events/Test/OwnerTestNotificationChannel.php index 904076aac2..efce1b2a80 100644 --- a/app/Events/Test/OwnerTestNotificationChannel.php +++ b/app/Events/Test/OwnerTestNotificationChannel.php @@ -31,7 +31,7 @@ class OwnerTestNotificationChannel { use SerializesModels; - public string $channel; + public string $channel; /** * Create a new event instance. diff --git a/app/Factory/TagFactory.php b/app/Factory/TagFactory.php index 2a8b29a037..892a6cfe37 100644 --- a/app/Factory/TagFactory.php +++ b/app/Factory/TagFactory.php @@ -34,7 +34,7 @@ use FireflyIII\User; */ class TagFactory { - private User $user; + private User $user; private UserGroup $userGroup; public function findOrCreate(string $tag): ?Tag diff --git a/app/Factory/TransactionGroupFactory.php b/app/Factory/TransactionGroupFactory.php index 3d347e4b20..10ecf31b52 100644 --- a/app/Factory/TransactionGroupFactory.php +++ b/app/Factory/TransactionGroupFactory.php @@ -36,8 +36,8 @@ use FireflyIII\User; class TransactionGroupFactory { private readonly TransactionJournalFactory $journalFactory; - private User $user; - private UserGroup $userGroup; + private User $user; + private UserGroup $userGroup; /** * TransactionGroupFactory constructor. diff --git a/app/Factory/TransactionJournalFactory.php b/app/Factory/TransactionJournalFactory.php index f2d716bbc4..fe17fa054d 100644 --- a/app/Factory/TransactionJournalFactory.php +++ b/app/Factory/TransactionJournalFactory.php @@ -39,9 +39,9 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Repositories\TransactionType\TransactionTypeRepositoryInterface; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Services\Internal\Destroy\JournalDestroyService; use FireflyIII\Services\Internal\Support\JournalServiceTrait; use FireflyIII\Support\Facades\FireflyConfig; @@ -70,7 +70,7 @@ class TransactionJournalFactory private PiggyBankRepositoryInterface $piggyRepository; private TransactionTypeRepositoryInterface $typeRepository; private User $user; - private UserGroup $userGroup; + private UserGroup $userGroup; /** * Constructor. @@ -414,18 +414,6 @@ class TransactionJournalFactory $this->accountRepository->setUser($this->user); } - public function setUserGroup(UserGroup $userGroup): void - { - $this->userGroup = $userGroup; - $this->currencyRepository->setUserGroup($userGroup); - $this->tagFactory->setUserGroup($userGroup); - $this->billRepository->setUserGroup($userGroup); - $this->budgetRepository->setUserGroup($userGroup); - $this->categoryRepository->setUserGroup($userGroup); - $this->piggyRepository->setUserGroup($userGroup); - $this->accountRepository->setUserGroup($userGroup); - } - private function reconciliationSanityCheck(?Account $sourceAccount, ?Account $destinationAccount): array { app('log')->debug(sprintf('Now in %s', __METHOD__)); @@ -605,4 +593,16 @@ class TransactionJournalFactory app('log')->info('Will trigger duplication alert for this journal.'); } } + + public function setUserGroup(UserGroup $userGroup): void + { + $this->userGroup = $userGroup; + $this->currencyRepository->setUserGroup($userGroup); + $this->tagFactory->setUserGroup($userGroup); + $this->billRepository->setUserGroup($userGroup); + $this->budgetRepository->setUserGroup($userGroup); + $this->categoryRepository->setUserGroup($userGroup); + $this->piggyRepository->setUserGroup($userGroup); + $this->accountRepository->setUserGroup($userGroup); + } } diff --git a/app/Handlers/Events/UpdatedGroupEventHandler.php b/app/Handlers/Events/UpdatedGroupEventHandler.php index 1c86ba9106..a2e7209eb9 100644 --- a/app/Handlers/Events/UpdatedGroupEventHandler.php +++ b/app/Handlers/Events/UpdatedGroupEventHandler.php @@ -53,6 +53,55 @@ class UpdatedGroupEventHandler } + /** + * This method will make sure all source / destination accounts are the same. + */ + public function unifyAccounts(UpdatedTransactionGroup $updatedGroupEvent): void + { + $group = $updatedGroupEvent->transactionGroup; + if (1 === $group->transactionJournals->count()) { + return; + } + + // first journal: + /** @var null|TransactionJournal $first */ + $first = $group->transactionJournals() + ->orderBy('transaction_journals.date', 'DESC') + ->orderBy('transaction_journals.order', 'ASC') + ->orderBy('transaction_journals.id', 'DESC') + ->orderBy('transaction_journals.description', 'DESC') + ->first() + ; + + if (null === $first) { + Log::warning(sprintf('Group #%d has no transaction journals.', $group->id)); + + return; + } + + $all = $group->transactionJournals()->get()->pluck('id')->toArray(); + + /** @var Account $sourceAccount */ + $sourceAccount = $first->transactions()->where('amount', '<', '0')->first()->account; + + /** @var Account $destAccount */ + $destAccount = $first->transactions()->where('amount', '>', '0')->first()->account; + + $type = $first->transactionType->type; + if (TransactionTypeEnum::TRANSFER->value === $type || TransactionTypeEnum::WITHDRAWAL->value === $type) { + // set all source transactions to source account: + Transaction::whereIn('transaction_journal_id', $all) + ->where('amount', '<', 0)->update(['account_id' => $sourceAccount->id]) + ; + } + if (TransactionTypeEnum::TRANSFER->value === $type || TransactionTypeEnum::DEPOSIT->value === $type) { + // set all destination transactions to destination account: + Transaction::whereIn('transaction_journal_id', $all) + ->where('amount', '>', 0)->update(['account_id' => $destAccount->id]) + ; + } + } + /** * This method will check all the rules when a journal is updated. */ @@ -119,55 +168,6 @@ class UpdatedGroupEventHandler event(new RequestedSendWebhookMessages()); } - /** - * This method will make sure all source / destination accounts are the same. - */ - public function unifyAccounts(UpdatedTransactionGroup $updatedGroupEvent): void - { - $group = $updatedGroupEvent->transactionGroup; - if (1 === $group->transactionJournals->count()) { - return; - } - - // first journal: - /** @var null|TransactionJournal $first */ - $first = $group->transactionJournals() - ->orderBy('transaction_journals.date', 'DESC') - ->orderBy('transaction_journals.order', 'ASC') - ->orderBy('transaction_journals.id', 'DESC') - ->orderBy('transaction_journals.description', 'DESC') - ->first() - ; - - if (null === $first) { - Log::warning(sprintf('Group #%d has no transaction journals.', $group->id)); - - return; - } - - $all = $group->transactionJournals()->get()->pluck('id')->toArray(); - - /** @var Account $sourceAccount */ - $sourceAccount = $first->transactions()->where('amount', '<', '0')->first()->account; - - /** @var Account $destAccount */ - $destAccount = $first->transactions()->where('amount', '>', '0')->first()->account; - - $type = $first->transactionType->type; - if (TransactionTypeEnum::TRANSFER->value === $type || TransactionTypeEnum::WITHDRAWAL->value === $type) { - // set all source transactions to source account: - Transaction::whereIn('transaction_journal_id', $all) - ->where('amount', '<', 0)->update(['account_id' => $sourceAccount->id]) - ; - } - if (TransactionTypeEnum::TRANSFER->value === $type || TransactionTypeEnum::DEPOSIT->value === $type) { - // set all destination transactions to destination account: - Transaction::whereIn('transaction_journal_id', $all) - ->where('amount', '>', 0)->update(['account_id' => $destAccount->id]) - ; - } - } - private function updateRunningBalance(UpdatedTransactionGroup $event): void { Log::debug(__METHOD__); diff --git a/app/Handlers/Observer/AccountObserver.php b/app/Handlers/Observer/AccountObserver.php index b8f13c845d..b5f5c32443 100644 --- a/app/Handlers/Observer/AccountObserver.php +++ b/app/Handlers/Observer/AccountObserver.php @@ -29,8 +29,8 @@ use FireflyIII\Models\Attachment; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; -use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface; use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Http\Api\ExchangeRateConverter; use Illuminate\Support\Facades\DB; diff --git a/app/Handlers/Observer/AutoBudgetObserver.php b/app/Handlers/Observer/AutoBudgetObserver.php index 15b98ef141..8ff4ff8e1f 100644 --- a/app/Handlers/Observer/AutoBudgetObserver.php +++ b/app/Handlers/Observer/AutoBudgetObserver.php @@ -37,12 +37,6 @@ class AutoBudgetObserver $this->updateNativeAmount($autoBudget); } - public function updated(AutoBudget $autoBudget): void - { - Log::debug('Observe "updated" of an auto budget.'); - $this->updateNativeAmount($autoBudget); - } - private function updateNativeAmount(AutoBudget $autoBudget): void { if (!Amount::convertToNative($autoBudget->budget->user)) { @@ -59,4 +53,10 @@ class AutoBudgetObserver $autoBudget->saveQuietly(); Log::debug('Auto budget native amount is updated.'); } + + public function updated(AutoBudget $autoBudget): void + { + Log::debug('Observe "updated" of an auto budget.'); + $this->updateNativeAmount($autoBudget); + } } diff --git a/app/Handlers/Observer/AvailableBudgetObserver.php b/app/Handlers/Observer/AvailableBudgetObserver.php index c98d44d8b6..5e1e3ef890 100644 --- a/app/Handlers/Observer/AvailableBudgetObserver.php +++ b/app/Handlers/Observer/AvailableBudgetObserver.php @@ -37,12 +37,6 @@ class AvailableBudgetObserver $this->updateNativeAmount($availableBudget); } - public function updated(AvailableBudget $availableBudget): void - { - // Log::debug('Observe "updated" of an available budget.'); - $this->updateNativeAmount($availableBudget); - } - private function updateNativeAmount(AvailableBudget $availableBudget): void { if (!Amount::convertToNative($availableBudget->user)) { @@ -61,4 +55,10 @@ class AvailableBudgetObserver $availableBudget->saveQuietly(); Log::debug('Available budget native amount is updated.'); } + + public function updated(AvailableBudget $availableBudget): void + { + // Log::debug('Observe "updated" of an available budget.'); + $this->updateNativeAmount($availableBudget); + } } diff --git a/app/Handlers/Observer/BillObserver.php b/app/Handlers/Observer/BillObserver.php index 4c3bc8441c..5ab6549a77 100644 --- a/app/Handlers/Observer/BillObserver.php +++ b/app/Handlers/Observer/BillObserver.php @@ -41,25 +41,6 @@ class BillObserver $this->updateNativeAmount($bill); } - public function deleting(Bill $bill): void - { - $repository = app(AttachmentRepositoryInterface::class); - $repository->setUser($bill->user); - - // app('log')->debug('Observe "deleting" of a bill.'); - /** @var Attachment $attachment */ - foreach ($bill->attachments()->get() as $attachment) { - $repository->destroy($attachment); - } - $bill->notes()->delete(); - } - - public function updated(Bill $bill): void - { - // Log::debug('Observe "updated" of a bill.'); - $this->updateNativeAmount($bill); - } - private function updateNativeAmount(Bill $bill): void { if (!Amount::convertToNative($bill->user)) { @@ -78,4 +59,23 @@ class BillObserver $bill->saveQuietly(); Log::debug('Bill native amounts are updated.'); } + + public function deleting(Bill $bill): void + { + $repository = app(AttachmentRepositoryInterface::class); + $repository->setUser($bill->user); + + // app('log')->debug('Observe "deleting" of a bill.'); + /** @var Attachment $attachment */ + foreach ($bill->attachments()->get() as $attachment) { + $repository->destroy($attachment); + } + $bill->notes()->delete(); + } + + public function updated(Bill $bill): void + { + // Log::debug('Observe "updated" of a bill.'); + $this->updateNativeAmount($bill); + } } diff --git a/app/Handlers/Observer/BudgetLimitObserver.php b/app/Handlers/Observer/BudgetLimitObserver.php index 3e3dc08dfe..f67e6a37e9 100644 --- a/app/Handlers/Observer/BudgetLimitObserver.php +++ b/app/Handlers/Observer/BudgetLimitObserver.php @@ -37,12 +37,6 @@ class BudgetLimitObserver $this->updateNativeAmount($budgetLimit); } - public function updated(BudgetLimit $budgetLimit): void - { - Log::debug('Observe "updated" of a budget limit.'); - $this->updateNativeAmount($budgetLimit); - } - private function updateNativeAmount(BudgetLimit $budgetLimit): void { if (!Amount::convertToNative($budgetLimit->budget->user)) { @@ -61,4 +55,10 @@ class BudgetLimitObserver $budgetLimit->saveQuietly(); Log::debug('Budget limit native amounts are updated.'); } + + public function updated(BudgetLimit $budgetLimit): void + { + Log::debug('Observe "updated" of a budget limit.'); + $this->updateNativeAmount($budgetLimit); + } } diff --git a/app/Handlers/Observer/PiggyBankEventObserver.php b/app/Handlers/Observer/PiggyBankEventObserver.php index 3238c827ce..0f62454063 100644 --- a/app/Handlers/Observer/PiggyBankEventObserver.php +++ b/app/Handlers/Observer/PiggyBankEventObserver.php @@ -37,12 +37,6 @@ class PiggyBankEventObserver $this->updateNativeAmount($event); } - public function updated(PiggyBankEvent $event): void - { - Log::debug('Observe "updated" of a piggy bank event.'); - $this->updateNativeAmount($event); - } - private function updateNativeAmount(PiggyBankEvent $event): void { $user = $event->piggyBank->accounts()->first()?->user; @@ -65,4 +59,10 @@ class PiggyBankEventObserver $event->saveQuietly(); Log::debug('Piggy bank event native amount is updated.'); } + + public function updated(PiggyBankEvent $event): void + { + Log::debug('Observe "updated" of a piggy bank event.'); + $this->updateNativeAmount($event); + } } diff --git a/app/Handlers/Observer/PiggyBankObserver.php b/app/Handlers/Observer/PiggyBankObserver.php index c1495eea3d..47f2052a84 100644 --- a/app/Handlers/Observer/PiggyBankObserver.php +++ b/app/Handlers/Observer/PiggyBankObserver.php @@ -41,6 +41,26 @@ class PiggyBankObserver $this->updateNativeAmount($piggyBank); } + private function updateNativeAmount(PiggyBank $piggyBank): void + { + $group = $piggyBank->accounts()->first()?->user->userGroup; + if (null === $group) { + Log::debug(sprintf('No account(s) yet for piggy bank #%d.', $piggyBank->id)); + + return; + } + $userCurrency = app('amount')->getNativeCurrencyByUserGroup($group); + $piggyBank->native_target_amount = null; + if ($piggyBank->transactionCurrency->id !== $userCurrency->id) { + $converter = new ExchangeRateConverter(); + $converter->setIgnoreSettings(true); + $converter->setUserGroup($group); + $piggyBank->native_target_amount = $converter->convert($piggyBank->transactionCurrency, $userCurrency, today(), $piggyBank->target_amount); + } + $piggyBank->saveQuietly(); + Log::debug('Piggy bank native target amount is updated.'); + } + /** * Also delete related objects. */ @@ -67,24 +87,4 @@ class PiggyBankObserver Log::debug('Observe "updated" of a piggy bank.'); $this->updateNativeAmount($piggyBank); } - - private function updateNativeAmount(PiggyBank $piggyBank): void - { - $group = $piggyBank->accounts()->first()?->user->userGroup; - if (null === $group) { - Log::debug(sprintf('No account(s) yet for piggy bank #%d.', $piggyBank->id)); - - return; - } - $userCurrency = app('amount')->getNativeCurrencyByUserGroup($group); - $piggyBank->native_target_amount = null; - if ($piggyBank->transactionCurrency->id !== $userCurrency->id) { - $converter = new ExchangeRateConverter(); - $converter->setIgnoreSettings(true); - $converter->setUserGroup($group); - $piggyBank->native_target_amount = $converter->convert($piggyBank->transactionCurrency, $userCurrency, today(), $piggyBank->target_amount); - } - $piggyBank->saveQuietly(); - Log::debug('Piggy bank native target amount is updated.'); - } } diff --git a/app/Handlers/Observer/TransactionObserver.php b/app/Handlers/Observer/TransactionObserver.php index 172ded4226..9869b45dd3 100644 --- a/app/Handlers/Observer/TransactionObserver.php +++ b/app/Handlers/Observer/TransactionObserver.php @@ -48,24 +48,6 @@ class TransactionObserver $this->updateNativeAmount($transaction); } - public function deleting(?Transaction $transaction): void - { - app('log')->debug('Observe "deleting" of a transaction.'); - $transaction?->transactionJournal?->delete(); - } - - public function updated(Transaction $transaction): void - { - // Log::debug('Observe "updated" of a transaction.'); - if (true === config('firefly.feature_flags.running_balance_column') && true === self::$recalculate) { - if (1 === bccomp($transaction->amount, '0')) { - Log::debug('Trigger recalculateForJournal'); - AccountBalanceCalculator::recalculateForJournal($transaction->transactionJournal); - } - } - $this->updateNativeAmount($transaction); - } - private function updateNativeAmount(Transaction $transaction): void { if (!Amount::convertToNative($transaction->transactionJournal->user)) { @@ -92,4 +74,22 @@ class TransactionObserver $transaction->saveQuietly(); Log::debug('Transaction native amounts are updated.'); } + + public function deleting(?Transaction $transaction): void + { + app('log')->debug('Observe "deleting" of a transaction.'); + $transaction?->transactionJournal?->delete(); + } + + public function updated(Transaction $transaction): void + { + // Log::debug('Observe "updated" of a transaction.'); + if (true === config('firefly.feature_flags.running_balance_column') && true === self::$recalculate) { + if (1 === bccomp($transaction->amount, '0')) { + Log::debug('Trigger recalculateForJournal'); + AccountBalanceCalculator::recalculateForJournal($transaction->transactionJournal); + } + } + $this->updateNativeAmount($transaction); + } } diff --git a/app/Helpers/Collector/Extensions/AccountCollection.php b/app/Helpers/Collector/Extensions/AccountCollection.php index 38d6a96622..a8d408a15e 100644 --- a/app/Helpers/Collector/Extensions/AccountCollection.php +++ b/app/Helpers/Collector/Extensions/AccountCollection.php @@ -36,6 +36,89 @@ use Illuminate\Support\Facades\Log; */ trait AccountCollection { + #[\Override] + public function accountBalanceIs(string $direction, string $operator, string $value): GroupCollectorInterface + { + Log::warning(sprintf('GroupCollector will be SLOW: accountBalanceIs: "%s" "%s" "%s"', $direction, $operator, $value)); + + /** + * @param int $index + * @param array $object + * + * @return bool + */ + $filter = static function (array $object) use ($direction, $operator, $value): bool { + /** @var array $transaction */ + foreach ($object['transactions'] as $transaction) { + $key = sprintf('%s_account_id', $direction); + $accountId = $transaction[$key] ?? 0; + if (0 === $accountId) { + return false; + } + + // in theory, this could lead to finding other users accounts. + /** @var null|Account $account */ + $account = Account::find($accountId); + if (null === $account) { + continue; + } + // the balance must be found BEFORE the transaction date. + // so sub one second. This is not perfect, but works well enough. + $date = clone $transaction['date']; + $date->subSecond(); + Log::debug(sprintf('accountBalanceIs: Call finalAccountBalance with date/time "%s"', $date->toIso8601String())); + $balance = Steam::finalAccountBalance($account, $date); + $result = bccomp((string) $balance['balance'], $value); + Log::debug(sprintf('"%s" vs "%s" is %d', $balance['balance'], $value, $result)); + + switch ($operator) { + default: + Log::error(sprintf('GroupCollector: accountBalanceIs: unknown operator "%s"', $operator)); + + return false; + + case '==': + Log::debug('Expect result to be 0 (equal)'); + + return 0 === $result; + + case '!=': + Log::debug('Expect result to be -1 or 1 (not equal)'); + + return 0 !== $result; + + case '>': + Log::debug('Expect result to be 1 (greater then)'); + + return 1 === $result; + + case '>=': + Log::debug('Expect result to be 0 or 1 (greater then or equal)'); + + return -1 !== $result; + + case '<': + Log::debug('Expect result to be -1 (less than)'); + + return -1 === $result; + + case '<=': + Log::debug('Expect result to be -1 or 0 (less than or equal)'); + + return 1 !== $result; + } + // if($balance['balance'] $operator $value) { + + // } + } + + return false; + }; + $this->postFilters[] = $filter; + + return $this; + } + /** * These accounts must not be included. */ @@ -231,87 +314,4 @@ trait AccountCollection return $this; } - - #[\Override] - public function accountBalanceIs(string $direction, string $operator, string $value): GroupCollectorInterface - { - Log::warning(sprintf('GroupCollector will be SLOW: accountBalanceIs: "%s" "%s" "%s"', $direction, $operator, $value)); - - /** - * @param int $index - * @param array $object - * - * @return bool - */ - $filter = static function (array $object) use ($direction, $operator, $value): bool { - /** @var array $transaction */ - foreach ($object['transactions'] as $transaction) { - $key = sprintf('%s_account_id', $direction); - $accountId = $transaction[$key] ?? 0; - if (0 === $accountId) { - return false; - } - - // in theory, this could lead to finding other users accounts. - /** @var null|Account $account */ - $account = Account::find($accountId); - if (null === $account) { - continue; - } - // the balance must be found BEFORE the transaction date. - // so sub one second. This is not perfect, but works well enough. - $date = clone $transaction['date']; - $date->subSecond(); - Log::debug(sprintf('accountBalanceIs: Call finalAccountBalance with date/time "%s"', $date->toIso8601String())); - $balance = Steam::finalAccountBalance($account, $date); - $result = bccomp((string) $balance['balance'], $value); - Log::debug(sprintf('"%s" vs "%s" is %d', $balance['balance'], $value, $result)); - - switch ($operator) { - default: - Log::error(sprintf('GroupCollector: accountBalanceIs: unknown operator "%s"', $operator)); - - return false; - - case '==': - Log::debug('Expect result to be 0 (equal)'); - - return 0 === $result; - - case '!=': - Log::debug('Expect result to be -1 or 1 (not equal)'); - - return 0 !== $result; - - case '>': - Log::debug('Expect result to be 1 (greater then)'); - - return 1 === $result; - - case '>=': - Log::debug('Expect result to be 0 or 1 (greater then or equal)'); - - return -1 !== $result; - - case '<': - Log::debug('Expect result to be -1 (less than)'); - - return -1 === $result; - - case '<=': - Log::debug('Expect result to be -1 or 0 (less than or equal)'); - - return 1 !== $result; - } - // if($balance['balance'] $operator $value) { - - // } - } - - return false; - }; - $this->postFilters[] = $filter; - - return $this; - } } diff --git a/app/Helpers/Collector/Extensions/CollectorProperties.php b/app/Helpers/Collector/Extensions/CollectorProperties.php index cfa3a5f78d..ae21d08041 100644 --- a/app/Helpers/Collector/Extensions/CollectorProperties.php +++ b/app/Helpers/Collector/Extensions/CollectorProperties.php @@ -37,6 +37,7 @@ trait CollectorProperties /** @var array */ public array $sorting; + private array $booleanFields; private ?int $endRow; private bool $expandGroupSearch; private array $fields; @@ -55,7 +56,6 @@ trait CollectorProperties private HasMany $query; private ?int $startRow; private array $stringFields; - private array $booleanFields; /* * This array is used to collect ALL tags the user may search for (using 'setTags'). * This way the user can call 'setTags' multiple times and get a joined result. diff --git a/app/Helpers/Collector/Extensions/MetaCollection.php b/app/Helpers/Collector/Extensions/MetaCollection.php index 310b9ef320..37d99ee720 100644 --- a/app/Helpers/Collector/Extensions/MetaCollection.php +++ b/app/Helpers/Collector/Extensions/MetaCollection.php @@ -29,12 +29,12 @@ use FireflyIII\Models\Bill; use FireflyIII\Models\Budget; use FireflyIII\Models\Category; use FireflyIII\Models\Tag; +use FireflyIII\Models\TransactionJournal; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; -use FireflyIII\Models\TransactionJournal; /** * Trait MetaCollection diff --git a/app/Helpers/Collector/GroupCollector.php b/app/Helpers/Collector/GroupCollector.php index d47ac0d155..98f2ba1bff 100644 --- a/app/Helpers/Collector/GroupCollector.php +++ b/app/Helpers/Collector/GroupCollector.php @@ -900,16 +900,6 @@ class GroupCollector implements GroupCollectorInterface return $this; } - /** - * Limit results to a specific currency, only normal one. - */ - public function setNormalCurrency(TransactionCurrency $currency): GroupCollectorInterface - { - $this->query->where('source.transaction_currency_id', $currency->id); - - return $this; - } - public function setEndRow(int $endRow): self { $this->endRow = $endRow; @@ -957,6 +947,16 @@ class GroupCollector implements GroupCollectorInterface return $this; } + /** + * Limit results to a specific currency, only normal one. + */ + public function setNormalCurrency(TransactionCurrency $currency): GroupCollectorInterface + { + $this->query->where('source.transaction_currency_id', $currency->id); + + return $this; + } + /** * Set the page to get. */ @@ -1168,8 +1168,7 @@ class GroupCollector implements GroupCollectorInterface // include budget ID + name (if any) ->withBudgetInformation() // include bill ID + name (if any) - ->withBillInformation() - ; + ->withBillInformation(); return $this; } diff --git a/app/Helpers/Collector/GroupCollectorInterface.php b/app/Helpers/Collector/GroupCollectorInterface.php index 6ff39b81b1..8dab806e8c 100644 --- a/app/Helpers/Collector/GroupCollectorInterface.php +++ b/app/Helpers/Collector/GroupCollectorInterface.php @@ -41,6 +41,8 @@ use Illuminate\Support\Collection; */ interface GroupCollectorInterface { + public function accountBalanceIs(string $direction, string $operator, string $value): self; + /** * Get transactions with a specific amount. */ @@ -457,11 +459,6 @@ interface GroupCollectorInterface */ public function setCurrency(TransactionCurrency $currency): self; - /** - * Limit results to a specific currency, either foreign or normal one. - */ - public function setNormalCurrency(TransactionCurrency $currency): self; - /** * Set destination accounts. */ @@ -526,6 +523,11 @@ interface GroupCollectorInterface */ public function setMetaDateRange(Carbon $start, Carbon $end, string $field): self; + /** + * Limit results to a specific currency, either foreign or normal one. + */ + public function setNormalCurrency(TransactionCurrency $currency): self; + /** * Define which accounts can NOT be part of the source and destination transactions. */ @@ -737,6 +739,4 @@ interface GroupCollectorInterface public function yearIs(string $year): self; public function yearIsNot(string $year): self; - - public function accountBalanceIs(string $direction, string $operator, string $value): self; } diff --git a/app/Helpers/Report/NetWorth.php b/app/Helpers/Report/NetWorth.php index cea39dbf67..cc09678a6f 100644 --- a/app/Helpers/Report/NetWorth.php +++ b/app/Helpers/Report/NetWorth.php @@ -49,7 +49,7 @@ class NetWorth implements NetWorthInterface { private AccountRepositoryInterface $accountRepository; private CurrencyRepositoryInterface $currencyRepos; - private User $user; // @phpstan-ignore-line + private User $user; // @phpstan-ignore-line private ?UserGroup $userGroup = null; // @phpstan-ignore-line /** diff --git a/app/Helpers/Report/PopupReport.php b/app/Helpers/Report/PopupReport.php index f5e0b13757..9f0984da10 100644 --- a/app/Helpers/Report/PopupReport.php +++ b/app/Helpers/Report/PopupReport.php @@ -29,8 +29,8 @@ use FireflyIII\Models\Account; use FireflyIII\Models\Budget; use FireflyIII\Models\Category; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use Illuminate\Support\Collection; /** diff --git a/app/Http/Controllers/Account/IndexController.php b/app/Http/Controllers/Account/IndexController.php index 80cafd6990..99ae94aa15 100644 --- a/app/Http/Controllers/Account/IndexController.php +++ b/app/Http/Controllers/Account/IndexController.php @@ -121,6 +121,16 @@ class IndexController extends Controller return view('accounts.index', compact('objectType', 'inactivePage', 'subTitleIcon', 'subTitle', 'page', 'accounts')); } + private function subtract(array $startBalances, array $endBalances): array + { + $result = []; + foreach ($endBalances as $key => $value) { + $result[$key] = bcsub((string) $value, $startBalances[$key] ?? '0'); + } + + return $result; + } + /** * Show list of accounts. * @@ -199,14 +209,4 @@ class IndexController extends Controller return view('accounts.index', compact('objectType', 'inactiveCount', 'subTitleIcon', 'subTitle', 'page', 'accounts')); } - - private function subtract(array $startBalances, array $endBalances): array - { - $result = []; - foreach ($endBalances as $key => $value) { - $result[$key] = bcsub((string) $value, $startBalances[$key] ?? '0'); - } - - return $result; - } } diff --git a/app/Http/Controllers/Account/ReconcileController.php b/app/Http/Controllers/Account/ReconcileController.php index dccf39c835..48363ffa69 100644 --- a/app/Http/Controllers/Account/ReconcileController.php +++ b/app/Http/Controllers/Account/ReconcileController.php @@ -225,10 +225,10 @@ class ReconcileController extends Controller ] ); $submission = [ - 'user' => auth()->user(), - 'user_group' => auth()->user()->userGroup, - 'group_title' => null, - 'transactions' => [ + 'user' => auth()->user(), + 'user_group' => auth()->user()->userGroup, + 'group_title' => null, + 'transactions' => [ [ 'user' => auth()->user(), 'user_group' => auth()->user()->userGroup, diff --git a/app/Http/Controllers/Account/ShowController.php b/app/Http/Controllers/Account/ShowController.php index 7aec7bc667..b798d1a0be 100644 --- a/app/Http/Controllers/Account/ShowController.php +++ b/app/Http/Controllers/Account/ShowController.php @@ -33,7 +33,6 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\Debug\Timer; use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Http\Controllers\PeriodOverview; -use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment; use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; diff --git a/app/Http/Controllers/Budget/IndexController.php b/app/Http/Controllers/Budget/IndexController.php index cbaf9d35d5..a71146878d 100644 --- a/app/Http/Controllers/Budget/IndexController.php +++ b/app/Http/Controllers/Budget/IndexController.php @@ -287,11 +287,11 @@ class IndexController extends Controller // also calculate how much left from budgeted: $sums['left'][$currencyId] ??= [ - 'amount' => '0', - 'currency_id' => $budgeted['currency_id'], - 'currency_symbol' => $budgeted['currency_symbol'], - 'currency_decimal_places' => $budgeted['currency_decimal_places'], - ]; + 'amount' => '0', + 'currency_id' => $budgeted['currency_id'], + 'currency_symbol' => $budgeted['currency_symbol'], + 'currency_decimal_places' => $budgeted['currency_decimal_places'], + ]; } } diff --git a/app/Http/Controllers/Chart/AccountController.php b/app/Http/Controllers/Chart/AccountController.php index a01ba3f2ed..c264db1a2c 100644 --- a/app/Http/Controllers/Chart/AccountController.php +++ b/app/Http/Controllers/Chart/AccountController.php @@ -35,7 +35,6 @@ use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\CacheProperties; -use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Http\Controllers\AugumentData; use FireflyIII\Support\Http\Controllers\ChartGeneration; @@ -535,6 +534,17 @@ class AccountController extends Controller return response()->json($data); } + private function updateChartKeys(array $array, array $balances): array + { + foreach (array_keys($balances) as $key) { + $array[$key] ??= [ + 'key' => $key, + ]; + } + + return $array; + } + /** * Shows the balances for a given set of dates and accounts. * @@ -676,15 +686,4 @@ class AccountController extends Controller return response()->json($data); } - - private function updateChartKeys(array $array, array $balances): array - { - foreach (array_keys($balances) as $key) { - $array[$key] ??= [ - 'key' => $key, - ]; - } - - return $array; - } } diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index eea4e5b724..0ac831ba75 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -38,7 +38,6 @@ use FireflyIII\Repositories\Budget\NoBudgetRepositoryInterface; use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; use FireflyIII\Support\CacheProperties; use FireflyIII\Support\Chart\Budget\FrontpageChartGenerator; -use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Http\Controllers\AugumentData; use FireflyIII\Support\Http\Controllers\DateCalculation; use Illuminate\Http\JsonResponse; diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 3a88fd8f5b..b491bc06d3 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -197,11 +197,11 @@ class CategoryController extends Controller $chartData[$inKey] = [ - 'label' => sprintf('%s (%s)', (string) trans('firefly.earned'), $currencyInfo['currency_name']), - 'entries' => [], - 'type' => 'bar', - 'backgroundColor' => 'rgba(0, 141, 76, 0.5)', // green - ]; + 'label' => sprintf('%s (%s)', (string) trans('firefly.earned'), $currencyInfo['currency_name']), + 'entries' => [], + 'type' => 'bar', + 'backgroundColor' => 'rgba(0, 141, 76, 0.5)', // green + ]; // loop empty periods: foreach (array_keys($periods) as $period) { $label = $periods[$period]; diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 7bbd63c15d..2ac0db4408 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -51,13 +51,12 @@ abstract class Controller extends BaseController // fails on PHP < 8.4 public protected(set) string $name; - - protected string $dateTimeFormat; - protected string $monthAndDayFormat; - protected bool $convertToNative = false; + protected bool $convertToNative = false; + protected string $dateTimeFormat; protected ?TransactionCurrency $defaultCurrency; - protected string $monthFormat; - protected string $redirectUrl = '/'; + protected string $monthAndDayFormat; + protected string $monthFormat; + protected string $redirectUrl = '/'; /** * Controller constructor. @@ -94,8 +93,8 @@ abstract class Controller extends BaseController View::share('uploadSize', $uploadSize); // share is alpha, is beta - $isAlpha = false; - $isBeta = false; + $isAlpha = false; + $isBeta = false; $isDevelop = false; if (str_contains((string) config('firefly.version'), 'alpha')) { $isAlpha = true; @@ -120,16 +119,16 @@ abstract class Controller extends BaseController $this->monthAndDayFormat = (string) trans('config.month_and_day_js', [], $locale); $this->dateTimeFormat = (string) trans('config.date_time_js', [], $locale); $darkMode = 'browser'; - $this->defaultCurrency =null; + $this->defaultCurrency = null; // get shown-intro-preference: if (auth()->check()) { - $this->defaultCurrency = Amount::getNativeCurrency(); - $language = Steam::getLanguage(); - $locale = Steam::getLocale(); - $darkMode = app('preferences')->get('darkMode', 'browser')->data; - $this->convertToNative =Amount::convertToNative(); - $page = $this->getPageName(); - $shownDemo = $this->hasSeenDemo(); + $this->defaultCurrency = Amount::getNativeCurrency(); + $language = Steam::getLanguage(); + $locale = Steam::getLocale(); + $darkMode = app('preferences')->get('darkMode', 'browser')->data; + $this->convertToNative = Amount::convertToNative(); + $page = $this->getPageName(); + $shownDemo = $this->hasSeenDemo(); View::share('language', $language); View::share('locale', $locale); View::share('convertToNative', $this->convertToNative); diff --git a/app/Http/Controllers/DebugController.php b/app/Http/Controllers/DebugController.php index 62ba16aa7f..ece11b3d14 100644 --- a/app/Http/Controllers/DebugController.php +++ b/app/Http/Controllers/DebugController.php @@ -25,6 +25,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers; use Carbon\Carbon; +use Exception; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Exceptions\FireflyException; @@ -65,110 +66,6 @@ class DebugController extends Controller $this->middleware(IsDemoUser::class)->except(['displayError']); } - public function routes(Request $request): never - { - if (!auth()->user()->hasRole('owner')) { - throw new NotFoundHttpException(); - } - - /** @var iterable $routes */ - $routes = Route::getRoutes(); - - if ('true' === $request->get('api')) { - $collection = []; - $i = 0; - - echo 'PATHS="'; - - /** @var \Illuminate\Routing\Route $route */ - foreach ($routes as $route) { - ++$i; - // skip API and other routes. - if (!str_starts_with($route->uri(), 'api/v1') - ) { - continue; - } - // skip non GET routes - if (!in_array('GET', $route->methods(), true)) { - continue; - } - // no name route: - if (null === $route->getName()) { - var_dump($route); - - exit; - } - - echo substr($route->uri(), 3); - if (0 === $i % 5) { - echo '"
PATHS="${PATHS},'; - } - if (0 !== $i % 5) { - echo ','; - } - } - - exit; - } - - - - $return = []; - - /** @var \Illuminate\Routing\Route $route */ - foreach ($routes as $route) { - // skip API and other routes. - if ( - str_starts_with($route->uri(), 'api') - || str_starts_with($route->uri(), '_debugbar') - || str_starts_with($route->uri(), '_ignition') - || str_starts_with($route->uri(), 'oauth') - || str_starts_with($route->uri(), 'chart') - || str_starts_with($route->uri(), 'v1/jscript') - || str_starts_with($route->uri(), 'v2/jscript') - || str_starts_with($route->uri(), 'json') - || str_starts_with($route->uri(), 'sanctum') - ) { - continue; - } - // skip non GET routes - if (!in_array('GET', $route->methods(), true)) { - continue; - } - // no name route: - if (null === $route->getName()) { - var_dump($route); - - exit; - } - if (!str_contains($route->uri(), '{')) { - - $return[$route->getName()] = route($route->getName()); - - continue; - } - $params = []; - foreach ($route->parameterNames() as $name) { - $params[] = $this->getParameter($name); - } - $return[$route->getName()] = route($route->getName(), $params); - } - $count = 0; - echo '
'; - echo '

Routes

'; - echo sprintf('

%s

', $count); - foreach ($return as $name => $path) { - echo sprintf('%2$s
', $path, $name).PHP_EOL; - ++$count; - if (0 === $count % 10) { - echo '
'; - echo sprintf('

%s

', $count); - } - } - - exit; - } - /** * Show all possible errors. * @@ -316,10 +213,10 @@ class DebugController extends Controller app('log')->debug('Could not check build date, but thats ok.'); app('log')->warning($e->getMessage()); } - if ('' !== (string) env('BASE_IMAGE_BUILD')) { // @phpstan-ignore-line + if ('' !== (string) env('BASE_IMAGE_BUILD')) { // @phpstan-ignore-line $return['base_build'] = env('BASE_IMAGE_BUILD'); // @phpstan-ignore-line } - if ('' !== (string) env('BASE_IMAGE_DATE')) { // @phpstan-ignore-line + if ('' !== (string) env('BASE_IMAGE_DATE')) { // @phpstan-ignore-line $return['base_build_date'] = env('BASE_IMAGE_DATE'); // @phpstan-ignore-line } @@ -442,19 +339,107 @@ class DebugController extends Controller return implode(' ', $flags); } - /** - * Flash all types of messages. - * - * @return Redirector|RedirectResponse - */ - public function testFlash(Request $request) + public function routes(Request $request): never { - $request->session()->flash('success', 'This is a success message.'); - $request->session()->flash('info', 'This is an info message.'); - $request->session()->flash('warning', 'This is a warning.'); - $request->session()->flash('error', 'This is an error!'); + if (!auth()->user()->hasRole('owner')) { + throw new NotFoundHttpException(); + } - return redirect(route('home')); + /** @var iterable $routes */ + $routes = Route::getRoutes(); + + if ('true' === $request->get('api')) { + $collection = []; + $i = 0; + + echo 'PATHS="'; + + /** @var \Illuminate\Routing\Route $route */ + foreach ($routes as $route) { + ++$i; + // skip API and other routes. + if (!str_starts_with($route->uri(), 'api/v1') + ) { + continue; + } + // skip non GET routes + if (!in_array('GET', $route->methods(), true)) { + continue; + } + // no name route: + if (null === $route->getName()) { + var_dump($route); + + exit; + } + + echo substr($route->uri(), 3); + if (0 === $i % 5) { + echo '"
PATHS="${PATHS},'; + } + if (0 !== $i % 5) { + echo ','; + } + } + + exit; + } + + + $return = []; + + /** @var \Illuminate\Routing\Route $route */ + foreach ($routes as $route) { + // skip API and other routes. + if ( + str_starts_with($route->uri(), 'api') + || str_starts_with($route->uri(), '_debugbar') + || str_starts_with($route->uri(), '_ignition') + || str_starts_with($route->uri(), 'oauth') + || str_starts_with($route->uri(), 'chart') + || str_starts_with($route->uri(), 'v1/jscript') + || str_starts_with($route->uri(), 'v2/jscript') + || str_starts_with($route->uri(), 'json') + || str_starts_with($route->uri(), 'sanctum') + ) { + continue; + } + // skip non GET routes + if (!in_array('GET', $route->methods(), true)) { + continue; + } + // no name route: + if (null === $route->getName()) { + var_dump($route); + + exit; + } + if (!str_contains($route->uri(), '{')) { + + $return[$route->getName()] = route($route->getName()); + + continue; + } + $params = []; + foreach ($route->parameterNames() as $name) { + $params[] = $this->getParameter($name); + } + $return[$route->getName()] = route($route->getName(), $params); + } + $count = 0; + echo '
'; + echo '

Routes

'; + echo sprintf('

%s

', $count); + foreach ($return as $name => $path) { + echo sprintf('%2$s
', $path, $name).PHP_EOL; + ++$count; + if (0 === $count % 10) { + echo '
'; + echo sprintf('

%s

', $count); + } + } + + exit; } private function getParameter(string $name): string @@ -582,4 +567,19 @@ class DebugController extends Controller } } + + /** + * Flash all types of messages. + * + * @return Redirector|RedirectResponse + */ + public function testFlash(Request $request) + { + $request->session()->flash('success', 'This is a success message.'); + $request->session()->flash('info', 'This is an info message.'); + $request->session()->flash('warning', 'This is a warning.'); + $request->session()->flash('error', 'This is an error!'); + + return redirect(route('home')); + } } diff --git a/app/Http/Controllers/JavascriptController.php b/app/Http/Controllers/JavascriptController.php index 2fcc5b3aed..0ce2368bf4 100644 --- a/app/Http/Controllers/JavascriptController.php +++ b/app/Http/Controllers/JavascriptController.php @@ -142,7 +142,6 @@ class JavascriptController extends Controller return response() ->view('v2.javascript.variables', $data) - ->header('Content-Type', 'text/javascript') - ; + ->header('Content-Type', 'text/javascript'); } } diff --git a/app/Http/Controllers/TransactionCurrency/CreateController.php b/app/Http/Controllers/TransactionCurrency/CreateController.php index 84e30328db..49e8645775 100644 --- a/app/Http/Controllers/TransactionCurrency/CreateController.php +++ b/app/Http/Controllers/TransactionCurrency/CreateController.php @@ -27,8 +27,8 @@ namespace FireflyIII\Http\Controllers\TransactionCurrency; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\CurrencyFormRequest; -use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; diff --git a/app/Http/Controllers/TransactionCurrency/DeleteController.php b/app/Http/Controllers/TransactionCurrency/DeleteController.php index aa68b1d34e..c7cc7fb83a 100644 --- a/app/Http/Controllers/TransactionCurrency/DeleteController.php +++ b/app/Http/Controllers/TransactionCurrency/DeleteController.php @@ -26,8 +26,8 @@ namespace FireflyIII\Http\Controllers\TransactionCurrency; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; diff --git a/app/Http/Controllers/TransactionCurrency/EditController.php b/app/Http/Controllers/TransactionCurrency/EditController.php index 80789ac1d7..926e973f44 100644 --- a/app/Http/Controllers/TransactionCurrency/EditController.php +++ b/app/Http/Controllers/TransactionCurrency/EditController.php @@ -27,8 +27,8 @@ namespace FireflyIII\Http\Controllers\TransactionCurrency; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\CurrencyFormRequest; use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; diff --git a/app/Http/Controllers/TransactionCurrency/IndexController.php b/app/Http/Controllers/TransactionCurrency/IndexController.php index f0ae6910b4..e57cc16384 100644 --- a/app/Http/Controllers/TransactionCurrency/IndexController.php +++ b/app/Http/Controllers/TransactionCurrency/IndexController.php @@ -26,8 +26,8 @@ namespace FireflyIII\Http\Controllers\TransactionCurrency; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; use Illuminate\Contracts\View\Factory; use Illuminate\Http\Request; diff --git a/app/Http/Middleware/InterestingMessage.php b/app/Http/Middleware/InterestingMessage.php index a953365c99..49e013d5ed 100644 --- a/app/Http/Middleware/InterestingMessage.php +++ b/app/Http/Middleware/InterestingMessage.php @@ -94,15 +94,6 @@ class InterestingMessage return null !== $transactionGroupId && null !== $message; } - private function userGroupMessage(Request $request): bool - { - // get parameters from request. - $transactionGroupId = $request->get('user_group_id'); - $message = $request->get('message'); - - return null !== $transactionGroupId && null !== $message; - } - private function handleGroupMessage(Request $request): void { // get parameters from request. @@ -141,13 +132,13 @@ class InterestingMessage } } - private function accountMessage(Request $request): bool + private function userGroupMessage(Request $request): bool { // get parameters from request. - $accountId = $request->get('account_id'); - $message = $request->get('message'); + $transactionGroupId = $request->get('user_group_id'); + $message = $request->get('message'); - return null !== $accountId && null !== $message; + return null !== $transactionGroupId && null !== $message; } private function handleUserGroupMessage(Request $request): void @@ -188,6 +179,15 @@ class InterestingMessage } } + private function accountMessage(Request $request): bool + { + // get parameters from request. + $accountId = $request->get('account_id'); + $message = $request->get('message'); + + return null !== $accountId && null !== $message; + } + private function handleAccountMessage(Request $request): void { // get parameters from request. diff --git a/app/Http/Middleware/TrustProxies.php b/app/Http/Middleware/TrustProxies.php index b36aaa900e..a973d14e83 100644 --- a/app/Http/Middleware/TrustProxies.php +++ b/app/Http/Middleware/TrustProxies.php @@ -32,7 +32,8 @@ use Symfony\Component\HttpFoundation\Request; class TrustProxies extends Middleware { // After... - protected $headers = Request::HEADER_X_FORWARDED_FOR + protected $headers + = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO diff --git a/app/Jobs/CreateRecurringTransactions.php b/app/Jobs/CreateRecurringTransactions.php index 19fe3c7d97..20a14d3895 100644 --- a/app/Jobs/CreateRecurringTransactions.php +++ b/app/Jobs/CreateRecurringTransactions.php @@ -378,10 +378,10 @@ class CreateRecurringTransactions implements ShouldQueue } $array = [ - 'user' => $recurrence->user, - 'user_group' => $recurrence->user->userGroup, - 'group_title' => $groupTitle, - 'transactions' => $this->getTransactionData($recurrence, $repetition, $date), + 'user' => $recurrence->user, + 'user_group' => $recurrence->user->userGroup, + 'group_title' => $groupTitle, + 'transactions' => $this->getTransactionData($recurrence, $repetition, $date), ]; /** @var TransactionGroup $group */ diff --git a/app/Mail/InvitationMail.php b/app/Mail/InvitationMail.php index 416aef6432..a41e6dbc6d 100644 --- a/app/Mail/InvitationMail.php +++ b/app/Mail/InvitationMail.php @@ -35,6 +35,7 @@ class InvitationMail extends Mailable { use Queueable; use SerializesModels; + public string $host; /** diff --git a/app/Mail/ReportNewJournalsMail.php b/app/Mail/ReportNewJournalsMail.php index cb166b886d..e0a492fa47 100644 --- a/app/Mail/ReportNewJournalsMail.php +++ b/app/Mail/ReportNewJournalsMail.php @@ -40,7 +40,8 @@ class ReportNewJournalsMail extends Mailable { use Queueable; use SerializesModels; - public array $transformed; + + public array $transformed; /** * ConfirmEmailChangeMail constructor. diff --git a/app/Models/Account.php b/app/Models/Account.php index fd99e855f4..f7110c38b7 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -48,15 +48,15 @@ class Account extends Model protected $casts = [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'user_id' => 'integer', - 'user_group_id' => 'integer', - 'deleted_at' => 'datetime', - 'active' => 'boolean', - 'encrypted' => 'boolean', - 'virtual_balance' => 'string', - 'native_virtual_balance' => 'string', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'user_id' => 'integer', + 'user_group_id' => 'integer', + 'deleted_at' => 'datetime', + 'active' => 'boolean', + 'encrypted' => 'boolean', + 'virtual_balance' => 'string', + 'native_virtual_balance' => 'string', ]; protected $fillable = ['user_id', 'user_group_id', 'account_type_id', 'name', 'active', 'virtual_balance', 'iban', 'native_virtual_balance']; diff --git a/app/Models/Attachment.php b/app/Models/Attachment.php index 6630ffdf0b..c46326e7fe 100644 --- a/app/Models/Attachment.php +++ b/app/Models/Attachment.php @@ -42,12 +42,12 @@ class Attachment extends Model protected $casts = [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'deleted_at' => 'datetime', - 'uploaded' => 'boolean', - 'user_id' => 'integer', - 'user_group_id' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'uploaded' => 'boolean', + 'user_id' => 'integer', + 'user_group_id' => 'integer', ]; protected $fillable = ['attachable_id', 'attachable_type', 'user_id', 'user_group_id', 'md5', 'filename', 'mime', 'title', 'description', 'size', 'uploaded']; diff --git a/app/Models/AutoBudget.php b/app/Models/AutoBudget.php index 922931d5d4..85deb27d83 100644 --- a/app/Models/AutoBudget.php +++ b/app/Models/AutoBudget.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Deprecated; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; @@ -35,13 +36,13 @@ class AutoBudget extends Model use ReturnsIntegerIdTrait; use SoftDeletes; - #[\Deprecated] /** @deprecated */ + #[\Deprecated] /** @deprecated */ public const int AUTO_BUDGET_ADJUSTED = 3; - #[\Deprecated] /** @deprecated */ + #[\Deprecated] /** @deprecated */ public const int AUTO_BUDGET_RESET = 1; - #[\Deprecated] /** @deprecated */ + #[\Deprecated] /** @deprecated */ public const int AUTO_BUDGET_ROLLOVER = 2; protected $casts = [ diff --git a/app/Models/AvailableBudget.php b/app/Models/AvailableBudget.php index 55c16d3df5..043ee93cf5 100644 --- a/app/Models/AvailableBudget.php +++ b/app/Models/AvailableBudget.php @@ -41,16 +41,16 @@ class AvailableBudget extends Model protected $casts = [ - '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', + '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 $fillable = ['user_id', 'user_group_id', 'transaction_currency_id', 'amount', 'start_date', 'end_date', 'start_date_tz', 'end_date_tz', 'native_amount']; @@ -95,10 +95,11 @@ class AvailableBudget extends Model ); } - protected function transactionCurrencyId(): Attribute + protected function endDate(): Attribute { return Attribute::make( - get: static fn ($value) => (int) $value, + get: fn (string $value) => Carbon::parse($value), + set: fn (Carbon $value) => $value->format('Y-m-d'), ); } @@ -110,11 +111,10 @@ class AvailableBudget extends Model ); } - protected function endDate(): Attribute + protected function transactionCurrencyId(): Attribute { return Attribute::make( - get: fn (string $value) => Carbon::parse($value), - set: fn (Carbon $value) => $value->format('Y-m-d'), + get: static fn ($value) => (int) $value, ); } } diff --git a/app/Models/Bill.php b/app/Models/Bill.php index 273b9d0628..2e3ef309ea 100644 --- a/app/Models/Bill.php +++ b/app/Models/Bill.php @@ -44,21 +44,21 @@ class Bill extends Model protected $casts = [ - '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', + '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 $fillable diff --git a/app/Models/Budget.php b/app/Models/Budget.php index 42a42a39cf..ca1f083071 100644 --- a/app/Models/Budget.php +++ b/app/Models/Budget.php @@ -43,13 +43,13 @@ class Budget extends Model protected $casts = [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'deleted_at' => 'datetime', - 'active' => 'boolean', - 'encrypted' => 'boolean', - 'user_id' => 'integer', - 'user_group_id' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'active' => 'boolean', + 'encrypted' => 'boolean', + 'user_id' => 'integer', + 'user_group_id' => 'integer', ]; protected $fillable = ['user_id', 'user_group_id', 'name', 'active', 'order', 'user_group_id']; diff --git a/app/Models/BudgetLimit.php b/app/Models/BudgetLimit.php index c330ab4f5b..6f1c891182 100644 --- a/app/Models/BudgetLimit.php +++ b/app/Models/BudgetLimit.php @@ -40,13 +40,13 @@ class BudgetLimit extends Model protected $casts = [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'start_date' => SeparateTimezoneCaster::class, - 'end_date' => SeparateTimezoneCaster::class, - 'auto_budget' => 'boolean', - 'amount' => 'string', - 'native_amount' => 'string', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'start_date' => SeparateTimezoneCaster::class, + 'end_date' => SeparateTimezoneCaster::class, + 'auto_budget' => 'boolean', + 'amount' => 'string', + 'native_amount' => 'string', ]; protected $dispatchesEvents = [ diff --git a/app/Models/Category.php b/app/Models/Category.php index 2b0ab99837..a426ffa0a1 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -42,12 +42,12 @@ class Category extends Model protected $casts = [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'deleted_at' => 'datetime', - 'encrypted' => 'boolean', - 'user_id' => 'integer', - 'user_group_id' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'encrypted' => 'boolean', + 'user_id' => 'integer', + 'user_group_id' => 'integer', ]; protected $fillable = ['user_id', 'user_group_id', 'name']; diff --git a/app/Models/CurrencyExchangeRate.php b/app/Models/CurrencyExchangeRate.php index 9d6fb264ba..430a8c2464 100644 --- a/app/Models/CurrencyExchangeRate.php +++ b/app/Models/CurrencyExchangeRate.php @@ -40,15 +40,15 @@ class CurrencyExchangeRate extends Model protected $casts = [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'user_id' => 'integer', - 'user_group_id' => 'integer', - 'from_currency_id' => 'integer', - 'to_currency_id' => 'integer', - 'date' => SeparateTimezoneCaster::class, - 'rate' => 'string', - 'user_rate' => 'string', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'user_id' => 'integer', + 'user_group_id' => 'integer', + 'from_currency_id' => 'integer', + 'to_currency_id' => 'integer', + 'date' => SeparateTimezoneCaster::class, + 'rate' => 'string', + 'user_rate' => 'string', ]; protected $fillable = ['user_id', 'from_currency_id', 'to_currency_id', 'date', 'date_tz', 'rate']; diff --git a/app/Models/GroupMembership.php b/app/Models/GroupMembership.php index ef8c189f75..1be2ef9b1d 100644 --- a/app/Models/GroupMembership.php +++ b/app/Models/GroupMembership.php @@ -36,12 +36,13 @@ class GroupMembership extends Model use ReturnsIntegerIdTrait; use ReturnsIntegerUserIdTrait; - protected $casts = [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'user_id' => 'integer', - 'user_group_id' => 'integer', - ]; + protected $casts + = [ + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'user_id' => 'integer', + 'user_group_id' => 'integer', + ]; protected $fillable = ['user_id', 'user_group_id', 'user_role_id']; diff --git a/app/Models/InvitedUser.php b/app/Models/InvitedUser.php index 67a8853a00..da0d77e670 100644 --- a/app/Models/InvitedUser.php +++ b/app/Models/InvitedUser.php @@ -39,10 +39,10 @@ class InvitedUser extends Model protected $casts = [ - 'expires' => SeparateTimezoneCaster::class, - 'redeemed' => 'boolean', - 'user_id' => 'integer', - 'user_group_id' => 'integer', + 'expires' => SeparateTimezoneCaster::class, + 'redeemed' => 'boolean', + 'user_id' => 'integer', + 'user_group_id' => 'integer', ]; protected $fillable = ['user_group_id', 'user_id', 'email', 'invite_code', 'expires', 'expires_tz', 'redeemed']; diff --git a/app/Models/ObjectGroup.php b/app/Models/ObjectGroup.php index 6c37589ce6..a13614d01c 100644 --- a/app/Models/ObjectGroup.php +++ b/app/Models/ObjectGroup.php @@ -40,11 +40,11 @@ class ObjectGroup extends Model protected $casts = [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'user_id' => 'integer', - 'user_group_id' => 'integer', - 'deleted_at' => 'datetime', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'user_id' => 'integer', + 'user_group_id' => 'integer', + 'deleted_at' => 'datetime', ]; protected $fillable = ['title', 'order', 'user_id', 'user_group_id']; diff --git a/app/Models/Preference.php b/app/Models/Preference.php index 529c6d6694..dbf2ef212f 100644 --- a/app/Models/Preference.php +++ b/app/Models/Preference.php @@ -37,11 +37,11 @@ class Preference extends Model protected $casts = [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'data' => 'array', - 'user_id' => 'integer', - 'user_group_id' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'data' => 'array', + 'user_id' => 'integer', + 'user_group_id' => 'integer', ]; protected $fillable = ['user_id', 'data', 'name', 'user_group_id']; diff --git a/app/Models/Recurrence.php b/app/Models/Recurrence.php index 235501eb51..5d4ad39ccd 100644 --- a/app/Models/Recurrence.php +++ b/app/Models/Recurrence.php @@ -44,20 +44,20 @@ class Recurrence extends Model protected $casts = [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'deleted_at' => 'datetime', - 'title' => 'string', - 'id' => 'int', - 'description' => 'string', - 'first_date' => SeparateTimezoneCaster::class, - 'repeat_until' => SeparateTimezoneCaster::class, - 'latest_date' => SeparateTimezoneCaster::class, - 'repetitions' => 'int', - 'active' => 'bool', - 'apply_rules' => 'bool', - 'user_id' => 'integer', - 'user_group_id' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'title' => 'string', + 'id' => 'int', + 'description' => 'string', + 'first_date' => SeparateTimezoneCaster::class, + 'repeat_until' => SeparateTimezoneCaster::class, + 'latest_date' => SeparateTimezoneCaster::class, + 'repetitions' => 'int', + 'active' => 'bool', + 'apply_rules' => 'bool', + 'user_id' => 'integer', + 'user_group_id' => 'integer', ]; protected $fillable diff --git a/app/Models/RecurrenceRepetition.php b/app/Models/RecurrenceRepetition.php index 5d66eb7e30..a5bcfc7f17 100644 --- a/app/Models/RecurrenceRepetition.php +++ b/app/Models/RecurrenceRepetition.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Deprecated; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; @@ -35,16 +36,16 @@ class RecurrenceRepetition extends Model use ReturnsIntegerIdTrait; use SoftDeletes; - #[\Deprecated] /** @deprecated */ + #[\Deprecated] /** @deprecated */ public const int WEEKEND_DO_NOTHING = 1; - #[\Deprecated] /** @deprecated */ + #[\Deprecated] /** @deprecated */ public const int WEEKEND_SKIP_CREATION = 2; - #[\Deprecated] /** @deprecated */ + #[\Deprecated] /** @deprecated */ public const int WEEKEND_TO_FRIDAY = 3; - #[\Deprecated] /** @deprecated */ + #[\Deprecated] /** @deprecated */ public const int WEEKEND_TO_MONDAY = 4; protected $casts diff --git a/app/Models/Rule.php b/app/Models/Rule.php index 9c4e2f9a45..86d8c36f20 100644 --- a/app/Models/Rule.php +++ b/app/Models/Rule.php @@ -41,16 +41,16 @@ class Rule extends Model protected $casts = [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'deleted_at' => 'datetime', - 'active' => 'boolean', - 'order' => 'int', - 'stop_processing' => 'boolean', - 'id' => 'int', - 'strict' => 'boolean', - 'user_id' => 'integer', - 'user_group_id' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'active' => 'boolean', + 'order' => 'int', + 'stop_processing' => 'boolean', + 'id' => 'int', + 'strict' => 'boolean', + 'user_id' => 'integer', + 'user_group_id' => 'integer', ]; protected $fillable = ['rule_group_id', 'order', 'active', 'title', 'description', 'user_id', 'user_group_id', 'strict']; diff --git a/app/Models/RuleGroup.php b/app/Models/RuleGroup.php index 1f15b7fee6..02b6494bda 100644 --- a/app/Models/RuleGroup.php +++ b/app/Models/RuleGroup.php @@ -41,14 +41,14 @@ class RuleGroup extends Model protected $casts = [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'deleted_at' => 'datetime', - 'active' => 'boolean', - 'stop_processing' => 'boolean', - 'order' => 'int', - 'user_id' => 'integer', - 'user_group_id' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'active' => 'boolean', + 'stop_processing' => 'boolean', + 'order' => 'int', + 'user_id' => 'integer', + 'user_group_id' => 'integer', ]; protected $fillable = ['user_id', 'user_group_id', 'stop_processing', 'order', 'title', 'description', 'active']; diff --git a/app/Models/Tag.php b/app/Models/Tag.php index aa92e5e1b0..fed190c86a 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -42,15 +42,15 @@ class Tag extends Model protected $casts = [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'deleted_at' => 'datetime', - 'date' => SeparateTimezoneCaster::class, - 'zoomLevel' => 'int', - 'latitude' => 'float', - 'longitude' => 'float', - 'user_id' => 'integer', - 'user_group_id' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'date' => SeparateTimezoneCaster::class, + 'zoomLevel' => 'int', + 'latitude' => 'float', + 'longitude' => 'float', + 'user_id' => 'integer', + 'user_group_id' => 'integer', ]; protected $fillable = ['user_id', 'user_group_id', 'tag', 'date', 'date_tz', 'description', 'tagMode']; diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index 65aff28889..cad47192fa 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -41,21 +41,21 @@ class Transaction extends Model protected $casts = [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'deleted_at' => 'datetime', - 'identifier' => 'int', - 'encrypted' => 'boolean', // model does not have these fields though - 'bill_name_encrypted' => 'boolean', - 'reconciled' => 'boolean', - 'balance_dirty' => 'boolean', - 'balance_before' => 'string', - 'balance_after' => 'string', - 'date' => 'datetime', - 'amount' => 'string', - 'foreign_amount' => 'string', - 'native_amount' => 'string', - 'native_foreign_amount' => 'string', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'identifier' => 'int', + 'encrypted' => 'boolean', // model does not have these fields though + 'bill_name_encrypted' => 'boolean', + 'reconciled' => 'boolean', + 'balance_dirty' => 'boolean', + 'balance_before' => 'string', + 'balance_after' => 'string', + 'date' => 'datetime', + 'amount' => 'string', + 'foreign_amount' => 'string', + 'native_amount' => 'string', + 'native_foreign_amount' => 'string', ]; protected $fillable diff --git a/app/Models/TransactionCurrency.php b/app/Models/TransactionCurrency.php index f20211ff17..33f08ee01b 100644 --- a/app/Models/TransactionCurrency.php +++ b/app/Models/TransactionCurrency.php @@ -37,8 +37,8 @@ class TransactionCurrency extends Model use ReturnsIntegerIdTrait; use SoftDeletes; - public ?bool $userGroupNative = null; public ?bool $userGroupEnabled = null; + public ?bool $userGroupNative = null; protected $casts = [ 'created_at' => 'datetime', diff --git a/app/Models/TransactionGroup.php b/app/Models/TransactionGroup.php index dfddb2f66c..9128667f97 100644 --- a/app/Models/TransactionGroup.php +++ b/app/Models/TransactionGroup.php @@ -40,14 +40,14 @@ class TransactionGroup extends Model protected $casts = [ - 'id' => 'integer', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'deleted_at' => 'datetime', - 'title' => 'string', - 'date' => 'datetime', - 'user_id' => 'integer', - 'user_group_id' => 'integer', + 'id' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'title' => 'string', + 'date' => 'datetime', + 'user_id' => 'integer', + 'user_group_id' => 'integer', ]; protected $fillable = ['user_id', 'user_group_id', 'title']; diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index b777a87a0b..6c39101e1a 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -54,19 +54,19 @@ class TransactionJournal extends Model protected $casts = [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'deleted_at' => 'datetime', - 'date' => SeparateTimezoneCaster::class, - 'interest_date' => 'date', - 'book_date' => 'date', - 'process_date' => 'date', - 'order' => 'int', - 'tag_count' => 'int', - 'encrypted' => 'boolean', - 'completed' => 'boolean', - 'user_id' => 'integer', - 'user_group_id' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'date' => SeparateTimezoneCaster::class, + 'interest_date' => 'date', + 'book_date' => 'date', + 'process_date' => 'date', + 'order' => 'int', + 'tag_count' => 'int', + 'encrypted' => 'boolean', + 'completed' => 'boolean', + 'user_id' => 'integer', + 'user_group_id' => 'integer', ]; protected $fillable @@ -114,11 +114,6 @@ class TransactionJournal extends Model return $this->belongsTo(User::class); } - public function userGroup(): BelongsTo - { - return $this->belongsTo(UserGroup::class); - } - public function attachments(): MorphMany { return $this->morphMany(Attachment::class, 'attachable'); @@ -246,6 +241,11 @@ class TransactionJournal extends Model return $this->hasMany(Transaction::class); } + public function userGroup(): BelongsTo + { + return $this->belongsTo(UserGroup::class); + } + protected function order(): Attribute { return Attribute::make( diff --git a/app/Models/TransactionType.php b/app/Models/TransactionType.php index 3d5390b3da..06fcde6c17 100644 --- a/app/Models/TransactionType.php +++ b/app/Models/TransactionType.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Deprecated; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Model; @@ -35,25 +36,25 @@ class TransactionType extends Model use ReturnsIntegerIdTrait; use SoftDeletes; - #[\Deprecated] /** @deprecated */ + #[\Deprecated] /** @deprecated */ public const string DEPOSIT = 'Deposit'; - #[\Deprecated] /** @deprecated */ + #[\Deprecated] /** @deprecated */ public const string INVALID = 'Invalid'; - #[\Deprecated] /** @deprecated */ + #[\Deprecated] /** @deprecated */ public const string LIABILITY_CREDIT = 'Liability credit'; - #[\Deprecated] /** @deprecated */ + #[\Deprecated] /** @deprecated */ public const string OPENING_BALANCE = 'Opening balance'; - #[\Deprecated] /** @deprecated */ + #[\Deprecated] /** @deprecated */ public const string RECONCILIATION = 'Reconciliation'; - #[\Deprecated] /** @deprecated */ + #[\Deprecated] /** @deprecated */ public const string TRANSFER = 'Transfer'; - #[\Deprecated] /** @deprecated */ + #[\Deprecated] /** @deprecated */ public const string WITHDRAWAL = 'Withdrawal'; protected $casts diff --git a/app/Models/Webhook.php b/app/Models/Webhook.php index b70c509711..7932894770 100644 --- a/app/Models/Webhook.php +++ b/app/Models/Webhook.php @@ -44,12 +44,12 @@ class Webhook extends Model protected $casts = [ - 'active' => 'boolean', - 'trigger' => 'integer', - 'response' => 'integer', - 'delivery' => 'integer', - 'user_id' => 'integer', - 'user_group_id' => 'integer', + 'active' => 'boolean', + 'trigger' => 'integer', + 'response' => 'integer', + 'delivery' => 'integer', + 'user_id' => 'integer', + 'user_group_id' => 'integer', ]; protected $fillable = ['active', 'trigger', 'response', 'delivery', 'user_id', 'user_group_id', 'url', 'title', 'secret']; diff --git a/app/Policies/AccountPolicy.php b/app/Policies/AccountPolicy.php index 02a0d920c3..7897ca32bc 100644 --- a/app/Policies/AccountPolicy.php +++ b/app/Policies/AccountPolicy.php @@ -40,6 +40,14 @@ class AccountPolicy return $this->view($user, $account); } + /** + * TODO needs better authentication, also for group. + */ + public function view(User $user, Account $account): bool + { + return auth()->check() && $user->id === $account->user_id; + } + /** * Everybody can do this, but selection should limit to user. */ @@ -57,12 +65,4 @@ class AccountPolicy { return $this->view($user, $account); } - - /** - * TODO needs better authentication, also for group. - */ - public function view(User $user, Account $account): bool - { - return auth()->check() && $user->id === $account->user_id; - } } diff --git a/app/Providers/CurrencyServiceProvider.php b/app/Providers/CurrencyServiceProvider.php index 05b875811e..4f9c466cb8 100644 --- a/app/Providers/CurrencyServiceProvider.php +++ b/app/Providers/CurrencyServiceProvider.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Providers; use FireflyIII\Repositories\Currency\CurrencyRepository; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepository as GroupCurrencyRepository; +use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface as GroupCurrencyRepositoryInterface; use FireflyIII\Repositories\ExchangeRate\ExchangeRateRepository; use FireflyIII\Repositories\ExchangeRate\ExchangeRateRepositoryInterface; diff --git a/app/Providers/FireflySessionProvider.php b/app/Providers/FireflySessionProvider.php index 9fb080ea35..249325dd55 100644 --- a/app/Providers/FireflySessionProvider.php +++ b/app/Providers/FireflySessionProvider.php @@ -67,7 +67,7 @@ class FireflySessionProvider extends ServiceProvider // First, we will create the session manager which is responsible for the // creation of the various session drivers when they are needed by the // application instance, and will resolve them on a lazy load basis. - => $app->make('session')->driver() + => $app->make('session')->driver() ); } } diff --git a/app/Providers/SearchServiceProvider.php b/app/Providers/SearchServiceProvider.php index 56b1c86b40..10df8973e2 100644 --- a/app/Providers/SearchServiceProvider.php +++ b/app/Providers/SearchServiceProvider.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace FireflyIII\Providers; -use FireflyIII\Support\Search\QueryParser\GdbotsQueryParser; use FireflyIII\Support\Search\OperatorQuerySearch; +use FireflyIII\Support\Search\QueryParser\GdbotsQueryParser; use FireflyIII\Support\Search\QueryParser\QueryParser; use FireflyIII\Support\Search\QueryParser\QueryParserInterface; use FireflyIII\Support\Search\SearchInterface; diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index d7834bcf9f..c498f6a5b1 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -533,6 +533,38 @@ class AccountRepository implements AccountRepositoryInterface, UserGroupInterfac return null; } + #[\Override] + public function periodCollection(Account $account, Carbon $start, Carbon $end): array + { + return $account->transactions() + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_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) + ->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']) + ->toArray() + ; + + } + public function resetAccountOrder(): void { $sets = [ @@ -650,36 +682,4 @@ class AccountRepository implements AccountRepositoryInterface, UserGroupInterfac return $factory->create($data); } - - #[\Override] - public function periodCollection(Account $account, Carbon $start, Carbon $end): array - { - return $account->transactions() - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_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) - ->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']) - ->toArray() - ; - - } } diff --git a/app/Repositories/Account/AccountRepositoryInterface.php b/app/Repositories/Account/AccountRepositoryInterface.php index 6bfb568c28..8f54067bc5 100644 --- a/app/Repositories/Account/AccountRepositoryInterface.php +++ b/app/Repositories/Account/AccountRepositoryInterface.php @@ -71,8 +71,6 @@ interface AccountRepositoryInterface public function findByName(string $name, array $types): ?Account; - public function periodCollection(Account $account, Carbon $start, Carbon $end): array; - public function getAccountBalances(Account $account): Collection; public function getAccountCurrency(Account $account): ?TransactionCurrency; @@ -151,6 +149,8 @@ interface AccountRepositoryInterface */ public function oldestJournalDate(Account $account): ?Carbon; + public function periodCollection(Account $account, Carbon $start, Carbon $end): array; + /** * Reset order types of the mentioned accounts. */ diff --git a/app/Repositories/Category/NoCategoryRepository.php b/app/Repositories/Category/NoCategoryRepository.php index 8c687125f6..7ca31375bc 100644 --- a/app/Repositories/Category/NoCategoryRepository.php +++ b/app/Repositories/Category/NoCategoryRepository.php @@ -77,9 +77,9 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface, UserGroupIn $journalId = (int) $journal['transaction_journal_id']; $array[$currencyId]['categories'][0]['transaction_journals'][$journalId] = [ - 'amount' => app('steam')->negative($journal['amount']), - 'date' => $journal['date'], - ]; + 'amount' => app('steam')->negative($journal['amount']), + 'date' => $journal['date'], + ]; } return $array; @@ -123,9 +123,9 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface, UserGroupIn $journalId = (int) $journal['transaction_journal_id']; $array[$currencyId]['categories'][0]['transaction_journals'][$journalId] = [ - 'amount' => app('steam')->positive($journal['amount']), - 'date' => $journal['date'], - ]; + 'amount' => app('steam')->positive($journal['amount']), + 'date' => $journal['date'], + ]; } return $array; diff --git a/app/Repositories/Webhook/WebhookRepository.php b/app/Repositories/Webhook/WebhookRepository.php index 256d0d34e7..6d684b8984 100644 --- a/app/Repositories/Webhook/WebhookRepository.php +++ b/app/Repositories/Webhook/WebhookRepository.php @@ -79,9 +79,8 @@ class WebhookRepository implements WebhookRepositoryInterface, UserGroupInterfac ->where('webhook_messages.errored', 0) ->get(['webhook_messages.*']) ->filter( - static fn (WebhookMessage $message) - // @phpstan-ignore-line - => $message->webhookAttempts()->count() <= 2 + static fn (WebhookMessage $message) // @phpstan-ignore-line + => $message->webhookAttempts()->count() <= 2 )->splice(0, 3) ; } diff --git a/app/Rules/BelongsUser.php b/app/Rules/BelongsUser.php index d1a67e8ca8..564b030443 100644 --- a/app/Rules/BelongsUser.php +++ b/app/Rules/BelongsUser.php @@ -82,8 +82,6 @@ class BelongsUser implements ValidationRule { - - $count = PiggyBank::leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id') ->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id') ->where('piggy_banks.id', '=', $value) @@ -104,32 +102,6 @@ class BelongsUser implements ValidationRule return $count > 0; } - protected function countField(string $class, string $field, string $value): int - { - $value = trim($value); - $objects = []; - // get all objects belonging to user: - if (PiggyBank::class === $class) { - $objects = PiggyBank::leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id') - ->where('accounts.user_id', '=', auth()->user()->id)->get(['piggy_banks.*']) - ; - } - if (PiggyBank::class !== $class) { - $objects = $class::where('user_id', '=', auth()->user()->id)->get(); - } - $count = 0; - foreach ($objects as $object) { - $objectValue = trim((string) $object->{$field}); // @phpstan-ignore-line - app('log')->debug(sprintf('Comparing object "%s" with value "%s"', $objectValue, $value)); - if ($objectValue === $value) { - ++$count; - app('log')->debug(sprintf('Hit! Count is now %d', $count)); - } - } - - return $count; - } - private function validateBillId(int $value): bool { if (0 === $value) { @@ -158,6 +130,32 @@ class BelongsUser implements ValidationRule return 1 === $count; } + protected function countField(string $class, string $field, string $value): int + { + $value = trim($value); + $objects = []; + // get all objects belonging to user: + if (PiggyBank::class === $class) { + $objects = PiggyBank::leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id') + ->where('accounts.user_id', '=', auth()->user()->id)->get(['piggy_banks.*']) + ; + } + if (PiggyBank::class !== $class) { + $objects = $class::where('user_id', '=', auth()->user()->id)->get(); + } + $count = 0; + foreach ($objects as $object) { + $objectValue = trim((string) $object->{$field}); // @phpstan-ignore-line + app('log')->debug(sprintf('Comparing object "%s" with value "%s"', $objectValue, $value)); + if ($objectValue === $value) { + ++$count; + app('log')->debug(sprintf('Hit! Count is now %d', $count)); + } + } + + return $count; + } + private function validateBudgetId(int $value): bool { if (0 === $value) { diff --git a/app/Rules/IsAllowedGroupAction.php b/app/Rules/IsAllowedGroupAction.php index b671526366..46c717ef44 100644 --- a/app/Rules/IsAllowedGroupAction.php +++ b/app/Rules/IsAllowedGroupAction.php @@ -34,7 +34,7 @@ use Illuminate\Support\Facades\Log; class IsAllowedGroupAction implements ValidationRule { - private array $acceptedRoles; + private array $acceptedRoles; private readonly UserGroupRepositoryInterface $repository; public function __construct(private readonly string $className, private readonly string $methodName) diff --git a/app/Rules/UniqueIban.php b/app/Rules/UniqueIban.php index 1a4167ee3f..6c1e312d79 100644 --- a/app/Rules/UniqueIban.php +++ b/app/Rules/UniqueIban.php @@ -34,7 +34,7 @@ use Illuminate\Contracts\Validation\ValidationRule; */ class UniqueIban implements ValidationRule { - private array $expectedTypes; + private array $expectedTypes; /** * Create a new rule instance. @@ -153,10 +153,10 @@ class UniqueIban implements ValidationRule } $query = auth()->user() - ->accounts() - ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') - ->where('accounts.iban', $iban) - ->whereIn('account_types.type', $typesArray) + ->accounts() + ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') + ->where('accounts.iban', $iban) + ->whereIn('account_types.type', $typesArray) ; if (null !== $this->account) { diff --git a/app/Services/Internal/Support/AccountServiceTrait.php b/app/Services/Internal/Support/AccountServiceTrait.php index 75cb0ad95c..787a08c239 100644 --- a/app/Services/Internal/Support/AccountServiceTrait.php +++ b/app/Services/Internal/Support/AccountServiceTrait.php @@ -662,10 +662,10 @@ trait AccountServiceTrait // submit to factory: $submission = [ - 'group_title' => null, - 'user' => $account->user, - 'user_group' => $account->user->userGroup, - 'transactions' => [ + 'group_title' => null, + 'user' => $account->user, + 'user_group' => $account->user->userGroup, + 'transactions' => [ [ 'type' => 'Opening balance', 'date' => $openingBalanceDate, diff --git a/app/Services/Internal/Support/JournalServiceTrait.php b/app/Services/Internal/Support/JournalServiceTrait.php index f4e27a24de..507f65e95e 100644 --- a/app/Services/Internal/Support/JournalServiceTrait.php +++ b/app/Services/Internal/Support/JournalServiceTrait.php @@ -286,7 +286,6 @@ trait JournalServiceTrait } - // $data['name'] = $data['name'] ?? '(no name)'; $account = $this->accountRepository->store( diff --git a/app/Support/Amount.php b/app/Support/Amount.php index e4b792acd7..7fc77c0229 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -48,61 +48,6 @@ class Amount return $this->formatFlat($format->symbol, $format->decimal_places, $amount, $coloured); } - /** - * 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 - { - $convertToNative = $this->convertToNative(); - $currency = $this->getNativeCurrency(); - $field = $convertToNative && $currency->id !== $journal['currency_id'] ? 'native_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 ($convertToNative && 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 convertToNative(?User $user = null): bool - { - if (null === $user) { - return true === Preferences::get('convert_to_native', false)->data && true === config('cer.enabled'); - // Log::debug(sprintf('convertToNative [a]: %s', var_export($result, true))); - } - - return true === Preferences::getForUser($user, 'convert_to_native', false)->data && true === config('cer.enabled'); - // Log::debug(sprintf('convertToNative [b]: %s', var_export($result, true))); - } - - /** - * 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 - { - $convertToNative = $this->convertToNative(); - $currency = $this->getNativeCurrency(); - $field = $convertToNative && $currency->id !== $journal->transaction_currency_id ? 'native_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; - } - /** * 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. @@ -147,20 +92,35 @@ class Amount return TransactionCurrency::orderBy('code', 'ASC')->get(); } - public function getCurrencies(): Collection + /** + * 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 { - /** @var User $user */ - $user = auth()->user(); + $convertToNative = $this->convertToNative(); + $currency = $this->getNativeCurrency(); + $field = $convertToNative && $currency->id !== $journal['currency_id'] ? 'native_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 ($convertToNative && 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 $user->currencies()->orderBy('code', 'ASC')->get(); + return (string) $amount; } - /** - * @deprecated - */ - public function getDefaultCurrency(): TransactionCurrency + public function convertToNative(?User $user = null): bool { - return $this->getNativeCurrency(); + if (null === $user) { + return true === Preferences::get('convert_to_native', false)->data && true === config('cer.enabled'); + // Log::debug(sprintf('convertToNative [a]: %s', var_export($result, true))); + } + + return true === Preferences::getForUser($user, 'convert_to_native', false)->data && true === config('cer.enabled'); + // Log::debug(sprintf('convertToNative [b]: %s', var_export($result, true))); } public function getNativeCurrency(): TransactionCurrency @@ -176,14 +136,6 @@ class Amount return $this->getSystemCurrency(); } - /** - * @deprecated - */ - public function getDefaultCurrencyByUserGroup(UserGroup $userGroup): TransactionCurrency - { - return $this->getNativeCurrencyByUserGroup($userGroup); - } - public function getNativeCurrencyByUserGroup(UserGroup $userGroup): TransactionCurrency { $cache = new CacheProperties(); @@ -210,6 +162,46 @@ class Amount return TransactionCurrency::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 + { + $convertToNative = $this->convertToNative(); + $currency = $this->getNativeCurrency(); + $field = $convertToNative && $currency->id !== $journal->transaction_currency_id ? 'native_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(); + } + + /** + * @deprecated + */ + public function getDefaultCurrency(): TransactionCurrency + { + return $this->getNativeCurrency(); + } + /** * @deprecated use getDefaultCurrencyByUserGroup instead */ @@ -218,6 +210,14 @@ class Amount return $this->getDefaultCurrencyByUserGroup($user->userGroup); } + /** + * @deprecated + */ + public function getDefaultCurrencyByUserGroup(UserGroup $userGroup): TransactionCurrency + { + return $this->getNativeCurrencyByUserGroup($userGroup); + } + /** * This method returns the correct format rules required by accounting.js, * the library used to format amounts in charts. diff --git a/app/Support/Authentication/RemoteUserGuard.php b/app/Support/Authentication/RemoteUserGuard.php index 31d4874d52..c3aa19cbda 100644 --- a/app/Support/Authentication/RemoteUserGuard.php +++ b/app/Support/Authentication/RemoteUserGuard.php @@ -38,8 +38,8 @@ use Illuminate\Support\Facades\Log; */ class RemoteUserGuard implements Guard { - protected Application $application; - protected ?User $user; + protected Application $application; + protected ?User $user; /** * Create a new authentication guard. diff --git a/app/Support/Chart/Budget/FrontpageChartGenerator.php b/app/Support/Chart/Budget/FrontpageChartGenerator.php index edcc4f260a..b0af8ae353 100644 --- a/app/Support/Chart/Budget/FrontpageChartGenerator.php +++ b/app/Support/Chart/Budget/FrontpageChartGenerator.php @@ -39,14 +39,14 @@ use Illuminate\Support\Facades\Log; */ class FrontpageChartGenerator { - protected OperationsRepositoryInterface $opsRepository; - private readonly BudgetLimitRepositoryInterface $blRepository; - private readonly BudgetRepositoryInterface $budgetRepository; - private Carbon $end; - private string $monthAndDayFormat; - private Carbon $start; - public bool $convertToNative = false; - public TransactionCurrency $default; + public bool $convertToNative = false; + public TransactionCurrency $default; + protected OperationsRepositoryInterface $opsRepository; + private readonly BudgetLimitRepositoryInterface $blRepository; + private readonly BudgetRepositoryInterface $budgetRepository; + private Carbon $end; + private string $monthAndDayFormat; + private Carbon $start; /** * FrontpageChartGenerator constructor. @@ -120,8 +120,8 @@ class FrontpageChartGenerator 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 + $data[1]['entries'][$title] = 0; // left to spend + $data[2]['entries'][$title] = 0; // overspent } return $data; @@ -209,7 +209,7 @@ class FrontpageChartGenerator $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[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 diff --git a/app/Support/Chart/Category/FrontpageChartGenerator.php b/app/Support/Chart/Category/FrontpageChartGenerator.php index 12882ae050..0142097f82 100644 --- a/app/Support/Chart/Category/FrontpageChartGenerator.php +++ b/app/Support/Chart/Category/FrontpageChartGenerator.php @@ -43,13 +43,13 @@ class FrontpageChartGenerator { use AugumentData; + public bool $convertToNative = false; + public TransactionCurrency $defaultCurrency; private AccountRepositoryInterface $accountRepos; private array $currencies; private NoCategoryRepositoryInterface $noCatRepos; private OperationsRepositoryInterface $opsRepos; private CategoryRepositoryInterface $repository; - public bool $convertToNative = false; - public TransactionCurrency $defaultCurrency; /** * FrontpageChartGenerator constructor. diff --git a/app/Support/Export/ExportDataGenerator.php b/app/Support/Export/ExportDataGenerator.php index ca58d58384..78145d74e9 100644 --- a/app/Support/Export/ExportDataGenerator.php +++ b/app/Support/Export/ExportDataGenerator.php @@ -586,6 +586,14 @@ class ExportDataGenerator return $string; } + /** + * @SuppressWarnings("PHPMD.UnusedFormalParameter") + */ + public function get(string $key, mixed $default = null): mixed + { + return null; + } + /** * @throws CannotInsertRecord * @throws Exception @@ -717,14 +725,6 @@ class ExportDataGenerator return $string; } - /** - * @SuppressWarnings("PHPMD.UnusedFormalParameter") - */ - public function get(string $key, mixed $default = null): mixed - { - return null; - } - /** * @throws CannotInsertRecord * @throws Exception diff --git a/app/Support/Http/Api/AccountBalanceGrouped.php b/app/Support/Http/Api/AccountBalanceGrouped.php index d4763ea6ed..6246157049 100644 --- a/app/Support/Http/Api/AccountBalanceGrouped.php +++ b/app/Support/Http/Api/AccountBalanceGrouped.php @@ -36,16 +36,16 @@ use Illuminate\Support\Facades\Log; */ class AccountBalanceGrouped { - private array $accountIds; - private string $carbonFormat; + private array $accountIds; + private string $carbonFormat; private readonly ExchangeRateConverter $converter; - private array $currencies = []; - private array $data = []; - private TransactionCurrency $default; - private Carbon $end; - private array $journals = []; - private string $preferredRange; - private Carbon $start; + private array $currencies = []; + private array $data = []; + private TransactionCurrency $default; + private Carbon $end; + private array $journals = []; + private string $preferredRange; + private Carbon $start; public function __construct() { diff --git a/app/Support/Http/Api/ExchangeRateConverter.php b/app/Support/Http/Api/ExchangeRateConverter.php index e529def18f..2149545a69 100644 --- a/app/Support/Http/Api/ExchangeRateConverter.php +++ b/app/Support/Http/Api/ExchangeRateConverter.php @@ -53,11 +53,6 @@ class ExchangeRateConverter } } - public function setUserGroup(UserGroup $userGroup): void - { - $this->userGroup = $userGroup; - } - /** * @throws FireflyException */ @@ -284,6 +279,11 @@ class ExchangeRateConverter $this->ignoreSettings = $ignoreSettings; } + public function setUserGroup(UserGroup $userGroup): void + { + $this->userGroup = $userGroup; + } + public function summarize(): void { if (false === $this->enabled()) { diff --git a/app/Support/Http/Api/SummaryBalanceGrouped.php b/app/Support/Http/Api/SummaryBalanceGrouped.php index f9fbd65aac..cd281d02d7 100644 --- a/app/Support/Http/Api/SummaryBalanceGrouped.php +++ b/app/Support/Http/Api/SummaryBalanceGrouped.php @@ -30,12 +30,12 @@ use Illuminate\Support\Facades\Log; class SummaryBalanceGrouped { - private const string SUM = 'sum'; - private array $amounts = []; - private array $currencies; + private const string SUM = 'sum'; + private array $amounts = []; + private array $currencies; private readonly CurrencyRepositoryInterface $currencyRepository; - private TransactionCurrency $default; - private array $keys; + private TransactionCurrency $default; + private array $keys; public function __construct() { diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index 82b4165f22..b8e950f2ce 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -35,7 +35,6 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Support\CacheProperties; use FireflyIII\Support\Debug\Timer; -use Illuminate\Support\Collection; /** * Trait PeriodOverview. @@ -66,8 +65,8 @@ use Illuminate\Support\Collection; */ trait PeriodOverview { - protected JournalRepositoryInterface $journalRepos; protected AccountRepositoryInterface $accountRepository; + protected JournalRepositoryInterface $journalRepos; /** * This method returns "period entries", so nov-2015, dec-2015, etc etc (this depends on the users session range) @@ -109,14 +108,14 @@ trait PeriodOverview [$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), - ]; + '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), + ]; } $cache->store($entries); Timer::stop('account-period-total'); @@ -124,6 +123,25 @@ trait PeriodOverview return $entries; } + private function filterTransactionsByType(TransactionTypeEnum $type, array $transactions, Carbon $start, Carbon $end): array + { + $result = []; + + /** + * @var int $index + * @var array $item + */ + foreach ($transactions as $index => $item) { + $date = Carbon::parse($item['date']); + if ($item['type'] === $type->value && $date >= $start && $date <= $end) { + $result[] = $item; + unset($transactions[$index]); + } + } + + return [$transactions, $result]; + } + private function filterTransfers(string $direction, array $transactions, Carbon $start, Carbon $end): array { $result = []; @@ -149,76 +167,6 @@ trait PeriodOverview return [$transactions, $result]; } - private function filterTransactionsByType(TransactionTypeEnum $type, array $transactions, Carbon $start, Carbon $end): array - { - $result = []; - - /** - * @var int $index - * @var array $item - */ - foreach ($transactions as $index => $item) { - $date = Carbon::parse($item['date']); - if ($item['type'] === $type->value && $date >= $start && $date <= $end) { - $result[] = $item; - unset($transactions[$index]); - } - } - - return [$transactions, $result]; - } - - /** - * 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; - } - - /** - * 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 groupByCurrency(array $journals): array { $return = []; @@ -340,6 +288,23 @@ trait PeriodOverview 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. * @@ -603,15 +568,49 @@ 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), + ]; } 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/JsonApi/Enrichments/AccountEnrichment.php b/app/Support/JsonApi/Enrichments/AccountEnrichment.php index bab93a794d..33a3aa6d56 100644 --- a/app/Support/JsonApi/Enrichments/AccountEnrichment.php +++ b/app/Support/JsonApi/Enrichments/AccountEnrichment.php @@ -34,7 +34,6 @@ use FireflyIII\Models\Location; use FireflyIII\Models\Note; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\UserGroup; -use FireflyIII\Support\Facades\Balance; use FireflyIII\Support\Facades\Steam; use FireflyIII\User; use Illuminate\Database\Eloquent\Model; @@ -48,19 +47,18 @@ use Illuminate\Support\Facades\Log; */ class AccountEnrichment implements EnrichmentInterface { - private Collection $collection; - - private User $user; - private UserGroup $userGroup; - private TransactionCurrency $native; private array $accountIds; private array $accountTypeIds; private array $accountTypes; + private Collection $collection; private array $currencies; + private array $locations; private array $meta; - private array $openingBalances; + private TransactionCurrency $native; private array $notes; - private array $locations; + private array $openingBalances; + private User $user; + private UserGroup $userGroup; public function __construct() { @@ -109,6 +107,17 @@ class AccountEnrichment implements EnrichmentInterface return $this->collection; } + private function collectAccountIds(): void + { + /** @var Account $account */ + foreach ($this->collection as $account) { + $this->accountIds[] = (int) $account->id; + $this->accountTypeIds[] = (int) $account->account_type_id; + } + $this->accountIds = array_unique($this->accountIds); + $this->accountTypeIds = array_unique($this->accountTypeIds); + } + private function getAccountTypes(): void { $types = AccountType::whereIn('id', $this->accountTypeIds)->get(); @@ -119,15 +128,97 @@ class AccountEnrichment implements EnrichmentInterface } } - private function collectAccountIds(): void + private function collectMetaData(): void { - /** @var Account $account */ - foreach ($this->collection as $account) { - $this->accountIds[] = (int) $account->id; - $this->accountTypeIds[] = (int) $account->account_type_id; + $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->accountIds) + ->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; + } } - $this->accountIds = array_unique($this->accountIds); - $this->accountTypeIds = array_unique($this->accountTypeIds); + $currencies = TransactionCurrency::whereIn('id', array_keys($this->currencies))->get(); + foreach ($currencies as $currency) { + $this->currencies[(int) $currency->id] = $currency; + } + $this->currencies[0] = $this->native; + 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->accountIds) + ->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 collectLocations(): void + { + $locations = Location::query()->whereIn('locatable_id', $this->accountIds) + ->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 collectOpeningBalances(): 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; + } + + public function setUser(User $user): void + { + $this->user = $user; + $this->userGroup = $user->userGroup; } private function appendCollectedData(): void @@ -179,101 +270,8 @@ class AccountEnrichment implements EnrichmentInterface }); } - private function collectOpeningBalances(): 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'], - ]; - } - } - - private function collectLocations(): void - { - $locations = Location::query()->whereIn('locatable_id', $this->accountIds) - ->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->accountIds) - ->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; - } - } - $currencies = TransactionCurrency::whereIn('id', array_keys($this->currencies))->get(); - foreach ($currencies as $currency) { - $this->currencies[(int) $currency->id] = $currency; - } - $this->currencies[0] = $this->native; - foreach ($this->currencies as $id => $currency) { - if (true === $currency) { - throw new FireflyException(sprintf('Currency #%d not found.', $id)); - } - } - } - - public function setUserGroup(UserGroup $userGroup): void - { - $this->userGroup = $userGroup; - } - - public function setUser(User $user): void - { - $this->user = $user; - $this->userGroup = $user->userGroup; - } - public function setNative(TransactionCurrency $native): void { $this->native = $native; } - - private function collectNotes(): void - { - $notes = Note::query()->whereIn('noteable_id', $this->accountIds) - ->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))); - } } diff --git a/app/Support/JsonApi/Enrichments/EnrichmentInterface.php b/app/Support/JsonApi/Enrichments/EnrichmentInterface.php index 916ca3c058..0ccfb7c060 100644 --- a/app/Support/JsonApi/Enrichments/EnrichmentInterface.php +++ b/app/Support/JsonApi/Enrichments/EnrichmentInterface.php @@ -35,7 +35,7 @@ interface EnrichmentInterface public function enrichSingle(array|Model $model): array|Model; - public function setUserGroup(UserGroup $userGroup): void; - public function setUser(User $user): void; + + public function setUserGroup(UserGroup $userGroup): void; } diff --git a/app/Support/JsonApi/Enrichments/TransactionGroupEnrichment.php b/app/Support/JsonApi/Enrichments/TransactionGroupEnrichment.php index 45a79daac1..6e9b516235 100644 --- a/app/Support/JsonApi/Enrichments/TransactionGroupEnrichment.php +++ b/app/Support/JsonApi/Enrichments/TransactionGroupEnrichment.php @@ -42,16 +42,16 @@ use Illuminate\Support\Facades\Log; class TransactionGroupEnrichment implements EnrichmentInterface { - private Collection $collection; - private array $notes; - private array $tags; - private array $locations; - private array $journalIds; - private User $user; // @phpstan-ignore-line - private UserGroup $userGroup; // @phpstan-ignore-line - private array $metaData; - private readonly array $dateFields; - private array $attachmentCount; + private array $attachmentCount; + private Collection $collection; + private readonly array $dateFields; + private array $journalIds; + private array $locations; + private array $metaData; // @phpstan-ignore-line + private array $notes; // @phpstan-ignore-line + private array $tags; + private User $user; + private UserGroup $userGroup; public function __construct() { @@ -64,6 +64,20 @@ class TransactionGroupEnrichment implements EnrichmentInterface $this->dateFields = ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date']; } + #[\Override] + public function enrichSingle(array|Model $model): array|TransactionGroup + { + Log::debug(__METHOD__); + if (is_array($model)) { + $collection = new Collection([$model]); + $collection = $this->enrich($collection); + + return $collection->first(); + } + + throw new FireflyException('Cannot enrich single model.'); + } + #[\Override] public function enrich(Collection $collection): Collection { @@ -83,20 +97,6 @@ class TransactionGroupEnrichment implements EnrichmentInterface return $this->collection; } - #[\Override] - public function enrichSingle(array|Model $model): array|TransactionGroup - { - Log::debug(__METHOD__); - if (is_array($model)) { - $collection = new Collection([$model]); - $collection = $this->enrich($collection); - - return $collection->first(); - } - - throw new FireflyException('Cannot enrich single model.'); - } - private function collectJournalIds(): void { /** @var array $group */ @@ -108,17 +108,6 @@ class TransactionGroupEnrichment implements EnrichmentInterface $this->journalIds = array_unique($this->journalIds); } - public function setUserGroup(UserGroup $userGroup): void - { - $this->userGroup = $userGroup; - } - - public function setUser(User $user): void - { - $this->user = $user; - $this->userGroup = $user->userGroup; - } - private function collectNotes(): void { $notes = Note::query()->whereIn('noteable_id', $this->journalIds) @@ -247,4 +236,15 @@ class TransactionGroupEnrichment implements EnrichmentInterface return $item; }); } + + public function setUser(User $user): void + { + $this->user = $user; + $this->userGroup = $user->userGroup; + } + + public function setUserGroup(UserGroup $userGroup): void + { + $this->userGroup = $userGroup; + } } diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php index c21a9353a7..c17d7c5193 100644 --- a/app/Support/Navigation.php +++ b/app/Support/Navigation.php @@ -443,11 +443,11 @@ class Navigation } return match ($range) { - 'last7' => '1W', - 'last30', 'MTD' => '1M', - 'last90', 'QTD' => '3M', + 'last7' => '1W', + 'last30', 'MTD' => '1M', + 'last90', 'QTD' => '3M', 'last365', 'YTD' => '1Y', - default => $range, + default => $range, }; } diff --git a/app/Support/Report/Budget/BudgetReportGenerator.php b/app/Support/Report/Budget/BudgetReportGenerator.php index 96fffd63b4..219b1a76e4 100644 --- a/app/Support/Report/Budget/BudgetReportGenerator.php +++ b/app/Support/Report/Budget/BudgetReportGenerator.php @@ -43,16 +43,16 @@ use Illuminate\Support\Collection; */ class BudgetReportGenerator { - private Collection $accounts; + private Collection $accounts; private readonly BudgetLimitRepositoryInterface $blRepository; - private Collection $budgets; - private TransactionCurrency $currency; - private Carbon $end; + private Collection $budgets; + private TransactionCurrency $currency; + private Carbon $end; private readonly NoBudgetRepositoryInterface $nbRepository; private readonly OperationsRepositoryInterface $opsRepository; - private array $report; + private array $report; private readonly BudgetRepositoryInterface $repository; - private Carbon $start; + private Carbon $start; /** * BudgetReportGenerator constructor. @@ -213,16 +213,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/app/Support/Report/Category/CategoryReportGenerator.php b/app/Support/Report/Category/CategoryReportGenerator.php index e5947180b3..91f1470bb8 100644 --- a/app/Support/Report/Category/CategoryReportGenerator.php +++ b/app/Support/Report/Category/CategoryReportGenerator.php @@ -35,12 +35,12 @@ use Illuminate\Support\Collection; */ class CategoryReportGenerator { - private Collection $accounts; - private Carbon $end; + private Collection $accounts; + private Carbon $end; private readonly NoCategoryRepositoryInterface $noCatRepository; private readonly OperationsRepositoryInterface $opsRepository; - private array $report; - private Carbon $start; + private array $report; + private Carbon $start; /** * CategoryReportGenerator constructor. diff --git a/app/Support/Report/Summarizer/TransactionSummarizer.php b/app/Support/Report/Summarizer/TransactionSummarizer.php index 070723a779..606fefae11 100644 --- a/app/Support/Report/Summarizer/TransactionSummarizer.php +++ b/app/Support/Report/Summarizer/TransactionSummarizer.php @@ -31,9 +31,9 @@ use Illuminate\Support\Facades\Log; class TransactionSummarizer { - private User $user; - private TransactionCurrency $default; private bool $convertToNative = false; + private TransactionCurrency $default; + private User $user; public function __construct(?User $user = null) { @@ -163,9 +163,6 @@ class TransactionSummarizer $default = Amount::getNativeCurrencyByUserGroup($this->user->userGroup); - - - Log::debug(sprintf('groupByDirection(array, %s, %s).', $direction, $method)); foreach ($journals as $journal) { // currency diff --git a/app/Support/Repositories/UserGroup/UserGroupInterface.php b/app/Support/Repositories/UserGroup/UserGroupInterface.php index 0c86120c1d..8a087f4edf 100644 --- a/app/Support/Repositories/UserGroup/UserGroupInterface.php +++ b/app/Support/Repositories/UserGroup/UserGroupInterface.php @@ -31,15 +31,15 @@ use Illuminate\Contracts\Auth\Authenticatable; interface UserGroupInterface { - public function getUserGroup(): ?UserGroup; + public function checkUserGroupAccess(UserRoleEnum $role): bool; public function getUser(): ?User; - public function checkUserGroupAccess(UserRoleEnum $role): bool; - - public function setUserGroup(UserGroup $userGroup): void; + public function getUserGroup(): ?UserGroup; public function setUser(null|Authenticatable|User $user): void; + public function setUserGroup(UserGroup $userGroup): void; + public function setUserGroupById(int $userGroupId): void; } diff --git a/app/Support/Repositories/UserGroup/UserGroupTrait.php b/app/Support/Repositories/UserGroup/UserGroupTrait.php index e6de102771..eb30fa7f53 100644 --- a/app/Support/Repositories/UserGroup/UserGroupTrait.php +++ b/app/Support/Repositories/UserGroup/UserGroupTrait.php @@ -40,16 +40,6 @@ trait UserGroupTrait protected ?User $user = null; protected ?UserGroup $userGroup = null; - public function getUserGroup(): ?UserGroup - { - return $this->userGroup; - } - - public function getUser(): ?User - { - return $this->user; - } - public function checkUserGroupAccess(UserRoleEnum $role): bool { $result = $this->user->hasRoleInGroupOrOwner($this->userGroup, $role); @@ -63,15 +53,9 @@ trait UserGroupTrait return false; } - /** - * TODO This method does not check if the user has access to this particular user group. - */ - public function setUserGroup(UserGroup $userGroup): void + public function getUser(): ?User { - if (null === $this->user) { - Log::warning(sprintf('User is not set in repository %s', static::class)); - } - $this->userGroup = $userGroup; + return $this->user; } /** @@ -92,6 +76,22 @@ trait UserGroupTrait throw new FireflyException(sprintf('Object is of class %s, not User.', $user::class)); } + public function getUserGroup(): ?UserGroup + { + return $this->userGroup; + } + + /** + * TODO This method does not check if the user has access to this particular user group. + */ + public function setUserGroup(UserGroup $userGroup): void + { + if (null === $this->user) { + Log::warning(sprintf('User is not set in repository %s', static::class)); + } + $this->userGroup = $userGroup; + } + /** * @throws FireflyException */ diff --git a/app/Support/Request/ConvertsDataTypes.php b/app/Support/Request/ConvertsDataTypes.php index 2116abe1f7..0e65fe7343 100644 --- a/app/Support/Request/ConvertsDataTypes.php +++ b/app/Support/Request/ConvertsDataTypes.php @@ -326,6 +326,18 @@ trait ConvertsDataTypes return $carbon; } + protected function floatFromValue(?string $string): ?float + { + if (null === $string) { + return null; + } + if ('' === $string) { + return null; + } + + return (float) $string; + } + /** * Returns all data in the request, or omits the field if not set, * according to the config from the request. This is the way. @@ -376,18 +388,20 @@ trait ConvertsDataTypes } /** - * Parse to integer + * Return integer value, or NULL when it's not set. */ - protected function integerFromValue(?string $string): ?int + protected function nullableInteger(string $field): ?int { - if (null === $string) { - return null; - } - if ('' === $string) { + if (false === $this->has($field)) { return null; } - return (int) $string; + $value = (string) $this->get($field); + if ('' === $value) { + return null; + } + + return (int) $value; } protected function parseAccounts(mixed $array): array @@ -419,7 +433,10 @@ trait ConvertsDataTypes return $return; } - protected function floatFromValue(?string $string): ?float + /** + * Parse to integer + */ + protected function integerFromValue(?string $string): ?int { if (null === $string) { return null; @@ -428,23 +445,6 @@ trait ConvertsDataTypes return null; } - return (float) $string; - } - - /** - * Return integer value, or NULL when it's not set. - */ - protected function nullableInteger(string $field): ?int - { - if (false === $this->has($field)) { - return null; - } - - $value = (string) $this->get($field); - if ('' === $value) { - return null; - } - - return (int) $value; + return (int) $string; } } diff --git a/app/Support/Search/OperatorQuerySearch.php b/app/Support/Search/OperatorQuerySearch.php index 90c039913e..519258c1fa 100644 --- a/app/Support/Search/OperatorQuerySearch.php +++ b/app/Support/Search/OperatorQuerySearch.php @@ -37,14 +37,14 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; -use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; -use FireflyIII\Support\Search\QueryParser\QueryParserInterface; -use FireflyIII\Support\Search\QueryParser\Node; -use FireflyIII\Support\Search\QueryParser\FieldNode; -use FireflyIII\Support\Search\QueryParser\StringNode; -use FireflyIII\Support\Search\QueryParser\NodeGroup; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Support\ParseDateString; +use FireflyIII\Support\Search\QueryParser\FieldNode; +use FireflyIII\Support\Search\QueryParser\Node; +use FireflyIII\Support\Search\QueryParser\NodeGroup; +use FireflyIII\Support\Search\QueryParser\QueryParserInterface; +use FireflyIII\Support\Search\QueryParser\StringNode; use FireflyIII\User; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; @@ -56,26 +56,26 @@ use Illuminate\Support\Collection; */ class OperatorQuerySearch implements SearchInterface { - protected Carbon $date; + protected Carbon $date; private readonly AccountRepositoryInterface $accountRepository; private readonly BillRepositoryInterface $billRepository; private readonly BudgetRepositoryInterface $budgetRepository; private readonly CategoryRepositoryInterface $categoryRepository; - private GroupCollectorInterface $collector; + private GroupCollectorInterface $collector; private readonly CurrencyRepositoryInterface $currencyRepository; - private array $excludeTags; - private array $includeAnyTags; + private array $excludeTags; + private array $includeAnyTags; // added to fix #8632 - private array $includeTags; - private array $invalidOperators; - private int $limit; + private array $includeTags; + private array $invalidOperators; + private int $limit; private readonly Collection $operators; - private int $page; - private array $prohibitedWords; + private int $page; + private array $prohibitedWords; private readonly float $startTime; private readonly TagRepositoryInterface $tagRepository; private readonly array $validOperators; - private array $words; + private array $words; /** * OperatorQuerySearch constructor. @@ -122,16 +122,6 @@ class OperatorQuerySearch implements SearchInterface return implode(' ', $this->words); } - public function getWords(): array - { - return $this->words; - } - - public function getExcludedWords(): array - { - return $this->prohibitedWords; - } - /** * @throws FireflyException */ @@ -202,15 +192,6 @@ class OperatorQuerySearch implements SearchInterface } } - private function handleNodeGroup(NodeGroup $node, bool $flipProhibitedFlag): void - { - $prohibited = $node->isProhibited($flipProhibitedFlag); - - foreach ($node->getNodes() as $subNode) { - $this->handleSearchNode($subNode, $prohibited); - } - } - private function handleStringNode(StringNode $node, bool $flipProhibitedFlag): void { $string = $node->getValue(); @@ -2775,6 +2756,15 @@ class OperatorQuerySearch implements SearchInterface } } + 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; @@ -2835,6 +2825,16 @@ class OperatorQuerySearch implements SearchInterface } } + public function getWords(): array + { + return $this->words; + } + + public function getExcludedWords(): array + { + return $this->prohibitedWords; + } + public function setDate(Carbon $date): void { $this->date = $date; diff --git a/app/Support/Search/QueryParser/GdbotsQueryParser.php b/app/Support/Search/QueryParser/GdbotsQueryParser.php index a071c972fa..aa802e0df0 100644 --- a/app/Support/Search/QueryParser/GdbotsQueryParser.php +++ b/app/Support/Search/QueryParser/GdbotsQueryParser.php @@ -26,9 +26,9 @@ declare(strict_types=1); namespace FireflyIII\Support\Search\QueryParser; use FireflyIII\Exceptions\FireflyException; -use Gdbots\QueryParser\QueryParser as BaseQueryParser; -use Gdbots\QueryParser\Node as GdbotsNode; use Gdbots\QueryParser\Enum\BoolOperator; +use Gdbots\QueryParser\Node as GdbotsNode; +use Gdbots\QueryParser\QueryParser as BaseQueryParser; use Illuminate\Support\Facades\Log; class GdbotsQueryParser implements QueryParserInterface diff --git a/app/Support/Search/QueryParser/Node.php b/app/Support/Search/QueryParser/Node.php index f13331965c..31412cfdab 100644 --- a/app/Support/Search/QueryParser/Node.php +++ b/app/Support/Search/QueryParser/Node.php @@ -34,28 +34,6 @@ abstract class Node { protected bool $prohibited; - /** - * Returns the prohibited status of the node, optionally inverted based on flipFlag - * - * Flipping is used when a node is inside a NodeGroup that has a prohibited status itself, causing inversion of the - * query parts inside - * - * @param bool $flipFlag When true, inverts the prohibited status - * - * @return bool The (potentially inverted) prohibited status - */ - public function isProhibited(bool $flipFlag): bool - { - if ($flipFlag) { - // Log::debug(sprintf('This %s is (flipped) now prohibited: %s',get_class($this), var_export(!$this->prohibited, true))); - return !$this->prohibited; - } - - // Log::debug(sprintf('This %s is (not flipped) now prohibited: %s',get_class($this), var_export($this->prohibited, true))); - return $this->prohibited; - - } - public function equals(self $compare): bool { if ($compare->isProhibited(false) !== $this->isProhibited(false)) { @@ -87,4 +65,26 @@ abstract class Node return true; } + + /** + * Returns the prohibited status of the node, optionally inverted based on flipFlag + * + * Flipping is used when a node is inside a NodeGroup that has a prohibited status itself, causing inversion of the + * query parts inside + * + * @param bool $flipFlag When true, inverts the prohibited status + * + * @return bool The (potentially inverted) prohibited status + */ + public function isProhibited(bool $flipFlag): bool + { + if ($flipFlag) { + // Log::debug(sprintf('This %s is (flipped) now prohibited: %s',get_class($this), var_export(!$this->prohibited, true))); + return !$this->prohibited; + } + + // Log::debug(sprintf('This %s is (not flipped) now prohibited: %s',get_class($this), var_export($this->prohibited, true))); + return $this->prohibited; + + } } diff --git a/app/Support/Search/QueryParser/QueryParser.php b/app/Support/Search/QueryParser/QueryParser.php index cdca33c415..05225f0fb7 100644 --- a/app/Support/Search/QueryParser/QueryParser.php +++ b/app/Support/Search/QueryParser/QueryParser.php @@ -34,8 +34,8 @@ use Illuminate\Support\Facades\Log; */ class QueryParser implements QueryParserInterface { - private string $query; private int $position = 0; + private string $query; public function parse(string $query): NodeGroup { diff --git a/app/Support/Search/SearchInterface.php b/app/Support/Search/SearchInterface.php index bc0fa0fdb1..7aaef87c68 100644 --- a/app/Support/Search/SearchInterface.php +++ b/app/Support/Search/SearchInterface.php @@ -33,6 +33,8 @@ use Illuminate\Support\Collection; */ interface SearchInterface { + public function getExcludedWords(): array; + public function getInvalidOperators(): array; public function getModifiers(): Collection; @@ -43,8 +45,6 @@ interface SearchInterface public function getWordsAsString(): string; - public function getExcludedWords(): array; - public function hasModifiers(): bool; public function parseQuery(string $query): void; diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 060f204ec1..b6da6a44dd 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -40,21 +40,154 @@ use Illuminate\Support\Str; */ class Steam { - public function getAccountCurrency(Account $account): ?TransactionCurrency + /** + * https://stackoverflow.com/questions/1642614/how-to-ceil-floor-and-round-bcmath-numbers + */ + public function bcround(?string $number, int $precision = 0): string { - $type = $account->accountType->type; - $list = config('firefly.valid_currency_account_types'); - - // return null if not in this list. - if (!in_array($type, $list, true)) { - return null; + if (null === $number) { + return '0'; } - $result = $account->accountMeta()->where('name', 'currency_id')->first(); - if (null === $result) { - return null; + if ('' === trim($number)) { + return '0'; + } + // if the number contains "E", it's in scientific notation, so we need to convert it to a normal number first. + if (false !== stripos($number, 'e')) { + $number = sprintf('%.12f', $number); } - return TransactionCurrency::find((int) $result->data); + // 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 bcsub($number, '0.'.str_repeat('0', $precision).'5', $precision); + } + + return $number; + } + + public function filterAccountBalances(array $total, Account $account, bool $convertToNative, ?TransactionCurrency $currency = null): array + { + Log::debug(sprintf('filterAccountBalances(#%d)', $account->id)); + $return = []; + foreach ($total as $key => $value) { + $return[$key] = $this->filterAccountBalance($value, $account, $convertToNative, $currency); + } + Log::debug(sprintf('end of filterAccountBalances(#%d)', $account->id)); + + return $return; + } + + public function filterAccountBalance(array $set, Account $account, bool $convertToNative, ?TransactionCurrency $currency = null): array + { + Log::debug(sprintf('filterAccountBalance(#%d)', $account->id), $set); + if (0 === count($set)) { + Log::debug(sprintf('Return empty array for account #%d', $account->id)); + + return []; + } + $defaultCurrency = app('amount')->getNativeCurrency(); + if ($convertToNative) { + if ($defaultCurrency->id === $currency?->id) { + Log::debug(sprintf('Unset [%s] for account #%d (no longer unset "native_balance")', $defaultCurrency->code, $account->id)); + unset($set[$defaultCurrency->code]); + } + // todo rethink this logic. + if (null !== $currency && $defaultCurrency->id !== $currency->id) { + Log::debug(sprintf('Unset balance for account #%d', $account->id)); + unset($set['balance']); + } + + if (null === $currency) { + Log::debug(sprintf('Unset balance for account #%d', $account->id)); + unset($set['balance']); + } + } + + if (!$convertToNative) { + if (null === $currency) { + Log::debug(sprintf('Unset native_balance and make defaultCurrency balance the balance for account #%d', $account->id)); + $set['balance'] = $set[$defaultCurrency->code] ?? '0'; + unset($set[$defaultCurrency->code]); + } + + if (null !== $currency) { + Log::debug(sprintf('Unset [%s] + [%s] balance for account #%d', $defaultCurrency->code, $currency->code, $account->id)); + unset($set[$defaultCurrency->code], $set[$currency->code]); + } + } + + // put specific value first in array. + if (array_key_exists('native_balance', $set)) { + $set = ['native_balance' => $set['native_balance']] + $set; + } + if (array_key_exists('balance', $set)) { + $set = ['balance' => $set['balance']] + $set; + } + Log::debug(sprintf('Return #%d', $account->id), $set); + + return $set; + } + + public function filterSpaces(string $string): string + { + $search = [ + "\u{0001}", // start of heading + "\u{0002}", // start of text + "\u{0003}", // end of text + "\u{0004}", // end of transmission + "\u{0005}", // enquiry + "\u{0006}", // ACK + "\u{0007}", // BEL + "\u{0008}", // backspace + "\u{000E}", // shift out + "\u{000F}", // shift in + "\u{0010}", // data link escape + "\u{0011}", // DC1 + "\u{0012}", // DC2 + "\u{0013}", // DC3 + "\u{0014}", // DC4 + "\u{0015}", // NAK + "\u{0016}", // SYN + "\u{0017}", // ETB + "\u{0018}", // CAN + "\u{0019}", // EM + "\u{001A}", // SUB + "\u{001B}", // escape + "\u{001C}", // file separator + "\u{001D}", // group separator + "\u{001E}", // record separator + "\u{001F}", // unit separator + "\u{007F}", // DEL + "\u{00A0}", // non-breaking space + "\u{1680}", // ogham space mark + "\u{180E}", // mongolian vowel separator + "\u{2000}", // en quad + "\u{2001}", // em quad + "\u{2002}", // en space + "\u{2003}", // em space + "\u{2004}", // three-per-em space + "\u{2005}", // four-per-em space + "\u{2006}", // six-per-em space + "\u{2007}", // figure space + "\u{2008}", // punctuation space + "\u{2009}", // thin space + "\u{200A}", // hair space + "\u{200B}", // zero width space + "\u{202F}", // narrow no-break space + "\u{3000}", // ideographic space + "\u{FEFF}", // zero width no -break space + "\x20", // plain old normal space, + ' ', + ]; + + // clear zalgo text + $string = \Safe\preg_replace('/(\pM{2})\pM+/u', '\1', $string); + $string = \Safe\preg_replace('/\s+/', '', $string); + + return str_replace($search, '', $string); } public function finalAccountBalanceInRange(Account $account, Carbon $start, Carbon $end, bool $convertToNative): array @@ -178,104 +311,6 @@ class Steam return $balances; } - public function finalAccountsBalance(Collection $accounts, Carbon $date): array - { - Log::debug(sprintf('finalAccountsBalance: Call finalAccountBalance with date/time "%s"', $date->toIso8601String())); - $balances = []; - foreach ($accounts as $account) { - $balances[$account->id] = $this->finalAccountBalance($account, $date); - } - - return $balances; - } - - /** - * https://stackoverflow.com/questions/1642614/how-to-ceil-floor-and-round-bcmath-numbers - */ - public function bcround(?string $number, int $precision = 0): string - { - if (null === $number) { - return '0'; - } - if ('' === trim($number)) { - return '0'; - } - // if the number contains "E", it's in scientific notation, so we need to convert it to a normal number first. - if (false !== stripos($number, 'e')) { - $number = sprintf('%.12f', $number); - } - - // 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 bcsub($number, '0.'.str_repeat('0', $precision).'5', $precision); - } - - return $number; - } - - public function filterSpaces(string $string): string - { - $search = [ - "\u{0001}", // start of heading - "\u{0002}", // start of text - "\u{0003}", // end of text - "\u{0004}", // end of transmission - "\u{0005}", // enquiry - "\u{0006}", // ACK - "\u{0007}", // BEL - "\u{0008}", // backspace - "\u{000E}", // shift out - "\u{000F}", // shift in - "\u{0010}", // data link escape - "\u{0011}", // DC1 - "\u{0012}", // DC2 - "\u{0013}", // DC3 - "\u{0014}", // DC4 - "\u{0015}", // NAK - "\u{0016}", // SYN - "\u{0017}", // ETB - "\u{0018}", // CAN - "\u{0019}", // EM - "\u{001A}", // SUB - "\u{001B}", // escape - "\u{001C}", // file separator - "\u{001D}", // group separator - "\u{001E}", // record separator - "\u{001F}", // unit separator - "\u{007F}", // DEL - "\u{00A0}", // non-breaking space - "\u{1680}", // ogham space mark - "\u{180E}", // mongolian vowel separator - "\u{2000}", // en quad - "\u{2001}", // em quad - "\u{2002}", // en space - "\u{2003}", // em space - "\u{2004}", // three-per-em space - "\u{2005}", // four-per-em space - "\u{2006}", // six-per-em space - "\u{2007}", // figure space - "\u{2008}", // punctuation space - "\u{2009}", // thin space - "\u{200A}", // hair space - "\u{200B}", // zero width space - "\u{202F}", // narrow no-break space - "\u{3000}", // ideographic space - "\u{FEFF}", // zero width no -break space - "\x20", // plain old normal space, - ' ', - ]; - - // clear zalgo text - $string = \Safe\preg_replace('/(\pM{2})\pM+/u', '\1', $string); - $string = \Safe\preg_replace('/\s+/', '', $string); - - return str_replace($search, '', $string); - } - /** * Returns smaller than or equal to, so be careful with END OF DAY. * @@ -365,67 +400,21 @@ class Steam return $final; } - public function filterAccountBalances(array $total, Account $account, bool $convertToNative, ?TransactionCurrency $currency = null): array + public function getAccountCurrency(Account $account): ?TransactionCurrency { - Log::debug(sprintf('filterAccountBalances(#%d)', $account->id)); - $return = []; - foreach ($total as $key => $value) { - $return[$key] = $this->filterAccountBalance($value, $account, $convertToNative, $currency); + $type = $account->accountType->type; + $list = config('firefly.valid_currency_account_types'); + + // return null if not in this list. + if (!in_array($type, $list, true)) { + return null; } - Log::debug(sprintf('end of filterAccountBalances(#%d)', $account->id)); - - return $return; - } - - public function filterAccountBalance(array $set, Account $account, bool $convertToNative, ?TransactionCurrency $currency = null): array - { - Log::debug(sprintf('filterAccountBalance(#%d)', $account->id), $set); - if (0 === count($set)) { - Log::debug(sprintf('Return empty array for account #%d', $account->id)); - - return []; - } - $defaultCurrency = app('amount')->getNativeCurrency(); - if ($convertToNative) { - if ($defaultCurrency->id === $currency?->id) { - Log::debug(sprintf('Unset [%s] for account #%d (no longer unset "native_balance")', $defaultCurrency->code, $account->id)); - unset($set[$defaultCurrency->code]); - } - // todo rethink this logic. - if (null !== $currency && $defaultCurrency->id !== $currency->id) { - Log::debug(sprintf('Unset balance for account #%d', $account->id)); - unset($set['balance']); - } - - if (null === $currency) { - Log::debug(sprintf('Unset balance for account #%d', $account->id)); - unset($set['balance']); - } + $result = $account->accountMeta()->where('name', 'currency_id')->first(); + if (null === $result) { + return null; } - if (!$convertToNative) { - if (null === $currency) { - Log::debug(sprintf('Unset native_balance and make defaultCurrency balance the balance for account #%d', $account->id)); - $set['balance'] = $set[$defaultCurrency->code] ?? '0'; - unset($set[$defaultCurrency->code]); - } - - if (null !== $currency) { - Log::debug(sprintf('Unset [%s] + [%s] balance for account #%d', $defaultCurrency->code, $currency->code, $account->id)); - unset($set[$defaultCurrency->code], $set[$currency->code]); - } - } - - // put specific value first in array. - if (array_key_exists('native_balance', $set)) { - $set = ['native_balance' => $set['native_balance']] + $set; - } - if (array_key_exists('balance', $set)) { - $set = ['balance' => $set['balance']] + $set; - } - Log::debug(sprintf('Return #%d', $account->id), $set); - - return $set; + return TransactionCurrency::find((int) $result->data); } private function groupAndSumTransactions(array $array, string $group, string $field): array @@ -440,6 +429,34 @@ class Steam return $return; } + private function convertAllBalances(array $others, TransactionCurrency $native, Carbon $date): string + { + $total = '0'; + $converter = new ExchangeRateConverter(); + foreach ($others as $key => $amount) { + $currency = TransactionCurrency::where('code', $key)->first(); + if (null === $currency) { + continue; + } + $current = $converter->convert($currency, $native, $date, $amount); + Log::debug(sprintf('Convert %s %s to %s %s', $currency->code, $amount, $native->code, $current)); + $total = bcadd($current, $total); + } + + return $total; + } + + public function finalAccountsBalance(Collection $accounts, Carbon $date): array + { + Log::debug(sprintf('finalAccountsBalance: Call finalAccountBalance with date/time "%s"', $date->toIso8601String())); + $balances = []; + foreach ($accounts as $account) { + $balances[$account->id] = $this->finalAccountBalance($account, $date); + } + + return $balances; + } + /** * @throws FireflyException */ @@ -665,21 +682,4 @@ class Steam return $amount; } - - private function convertAllBalances(array $others, TransactionCurrency $native, Carbon $date): string - { - $total = '0'; - $converter = new ExchangeRateConverter(); - foreach ($others as $key => $amount) { - $currency = TransactionCurrency::where('code', $key)->first(); - if (null === $currency) { - continue; - } - $current = $converter->convert($currency, $native, $date, $amount); - Log::debug(sprintf('Convert %s %s to %s %s', $currency->code, $amount, $native->code, $current)); - $total = bcadd($current, $total); - } - - return $total; - } } diff --git a/app/Support/Twig/AmountFormat.php b/app/Support/Twig/AmountFormat.php index 7028a04a51..4cf3ac7b07 100644 --- a/app/Support/Twig/AmountFormat.php +++ b/app/Support/Twig/AmountFormat.php @@ -105,31 +105,6 @@ class AmountFormat extends AbstractExtension ); } - /** - * Use the code to format a currency. - */ - protected function formatAmountByCode(): TwigFunction - { - // formatAmountByCode - return new TwigFunction( - 'formatAmountByCode', - static function (string $amount, string $code, ?bool $coloured = null): string { - $coloured ??= true; - - /** @var null|TransactionCurrency $currency */ - $currency = TransactionCurrency::whereCode($code)->first(); - if (null === $currency) { - Log::error(sprintf('Could not find currency with code "%s". Fallback to native currency.', $code)); - $currency = Amount::getNativeCurrency(); - Log::error(sprintf('Fallback currency is "%s".', $currency->code)); - } - - return app('amount')->formatAnything($currency, $amount, $coloured); - }, - ['is_safe' => ['html']] - ); - } - /** * Will format the amount by the currency related to the given account. */ @@ -165,4 +140,29 @@ class AmountFormat extends AbstractExtension ['is_safe' => ['html']] ); } + + /** + * Use the code to format a currency. + */ + protected function formatAmountByCode(): TwigFunction + { + // formatAmountByCode + return new TwigFunction( + 'formatAmountByCode', + static function (string $amount, string $code, ?bool $coloured = null): string { + $coloured ??= true; + + /** @var null|TransactionCurrency $currency */ + $currency = TransactionCurrency::whereCode($code)->first(); + if (null === $currency) { + Log::error(sprintf('Could not find currency with code "%s". Fallback to native currency.', $code)); + $currency = Amount::getNativeCurrency(); + Log::error(sprintf('Fallback currency is "%s".', $currency->code)); + } + + return app('amount')->formatAnything($currency, $amount, $coloured); + }, + ['is_safe' => ['html']] + ); + } } diff --git a/app/Support/Twig/General.php b/app/Support/Twig/General.php index 80b4e1b482..37e3fb2b45 100644 --- a/app/Support/Twig/General.php +++ b/app/Support/Twig/General.php @@ -139,14 +139,14 @@ class General extends AbstractExtension return new TwigFilter( 'mimeIcon', static fn (string $string): string => match ($string) { - 'application/pdf' => 'fa-file-pdf-o', - 'image/png', 'image/jpeg', 'image/svg+xml', 'image/heic', 'image/heic-sequence', 'application/vnd.oasis.opendocument.image' => 'fa-file-image-o', + 'application/pdf' => 'fa-file-pdf-o', + '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', + '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']] ); diff --git a/app/TransactionRules/Actions/UpdatePiggyBank.php b/app/TransactionRules/Actions/UpdatePiggyBank.php index 5633ba6b83..6b7113b6b2 100644 --- a/app/TransactionRules/Actions/UpdatePiggyBank.php +++ b/app/TransactionRules/Actions/UpdatePiggyBank.php @@ -140,6 +140,29 @@ class UpdatePiggyBank implements ActionInterface return $repository->findByName($name); } + private function getAccounts(TransactionJournal $journal): array + { + return [ + 'source' => $journal->transactions()->where('amount', '<', '0')->first()?->account, + 'destination' => $journal->transactions()->where('amount', '>', '0')->first()?->account, + ]; + } + + private function isConnected(PiggyBank $piggyBank, ?Account $link): bool + { + if (null === $link) { + return false; + } + foreach ($piggyBank->accounts as $account) { + if ($account->id === $link->id) { + return true; + } + } + Log::debug(sprintf('Piggy bank is not connected to account #%d "%s"', $link->id, $link->name)); + + return false; + } + private function removeAmount(PiggyBank $piggyBank, array $array, TransactionJournal $journal, Account $account, string $amount): void { $repository = app(PiggyBankRepositoryInterface::class); @@ -208,27 +231,4 @@ class UpdatePiggyBank implements ActionInterface $repository->addAmount($piggyBank, $account, $amount, $journal); } - - private function getAccounts(TransactionJournal $journal): array - { - return [ - 'source' => $journal->transactions()->where('amount', '<', '0')->first()?->account, - 'destination' => $journal->transactions()->where('amount', '>', '0')->first()?->account, - ]; - } - - private function isConnected(PiggyBank $piggyBank, ?Account $link): bool - { - if (null === $link) { - return false; - } - foreach ($piggyBank->accounts as $account) { - if ($account->id === $link->id) { - return true; - } - } - Log::debug(sprintf('Piggy bank is not connected to account #%d "%s"', $link->id, $link->name)); - - return false; - } } diff --git a/app/TransactionRules/Engine/SearchRuleEngine.php b/app/TransactionRules/Engine/SearchRuleEngine.php index ce1398b3fc..385d8bdd14 100644 --- a/app/TransactionRules/Engine/SearchRuleEngine.php +++ b/app/TransactionRules/Engine/SearchRuleEngine.php @@ -45,11 +45,11 @@ use Illuminate\Support\Facades\Log; class SearchRuleEngine implements RuleEngineInterface { private readonly Collection $groups; - private array $operators; - private bool $refreshTriggers; - private array $resultCount; + private array $operators; + private bool $refreshTriggers; + private array $resultCount; private readonly Collection $rules; - private User $user; + private User $user; public function __construct() { diff --git a/app/TransactionRules/Expressions/ActionExpression.php b/app/TransactionRules/Expressions/ActionExpression.php index 97970c2634..a012c3c3b3 100644 --- a/app/TransactionRules/Expressions/ActionExpression.php +++ b/app/TransactionRules/Expressions/ActionExpression.php @@ -29,7 +29,7 @@ use Symfony\Component\ExpressionLanguage\SyntaxError; class ActionExpression { - private static array $NAMES + private static array $NAMES = [ // 'transaction_group_id', // 'user_id', diff --git a/app/Transformers/AccountTransformer.php b/app/Transformers/AccountTransformer.php index 935bc3104d..82c8fc9c04 100644 --- a/app/Transformers/AccountTransformer.php +++ b/app/Transformers/AccountTransformer.php @@ -40,9 +40,9 @@ use Symfony\Component\HttpFoundation\ParameterBag; */ class AccountTransformer extends AbstractTransformer { - protected AccountRepositoryInterface $repository; protected bool $convertToNative; protected TransactionCurrency $native; + protected AccountRepositoryInterface $repository; /** * AccountTransformer constructor. diff --git a/app/Transformers/AttachmentTransformer.php b/app/Transformers/AttachmentTransformer.php index a0a2fa8670..dba5661a7d 100644 --- a/app/Transformers/AttachmentTransformer.php +++ b/app/Transformers/AttachmentTransformer.php @@ -50,21 +50,21 @@ class AttachmentTransformer extends AbstractTransformer $this->repository->setUser($attachment->user); return [ - 'id' => (string) $attachment->id, - 'created_at' => $attachment->created_at->toAtomString(), - 'updated_at' => $attachment->updated_at->toAtomString(), - 'attachable_id' => (string) $attachment->attachable_id, - 'attachable_type' => str_replace('FireflyIII\Models\\', '', $attachment->attachable_type), - 'md5' => $attachment->md5, - 'hash' => $attachment->md5, - 'filename' => $attachment->filename, - 'download_url' => route('api.v1.attachments.download', [$attachment->id]), - 'upload_url' => route('api.v1.attachments.upload', [$attachment->id]), - 'title' => $attachment->title, - 'notes' => $this->repository->getNoteText($attachment), - 'mime' => $attachment->mime, - 'size' => (int) $attachment->size, - 'links' => [ + 'id' => (string) $attachment->id, + 'created_at' => $attachment->created_at->toAtomString(), + 'updated_at' => $attachment->updated_at->toAtomString(), + 'attachable_id' => (string) $attachment->attachable_id, + 'attachable_type' => str_replace('FireflyIII\Models\\', '', $attachment->attachable_type), + 'md5' => $attachment->md5, + 'hash' => $attachment->md5, + 'filename' => $attachment->filename, + 'download_url' => route('api.v1.attachments.download', [$attachment->id]), + 'upload_url' => route('api.v1.attachments.upload', [$attachment->id]), + 'title' => $attachment->title, + 'notes' => $this->repository->getNoteText($attachment), + 'mime' => $attachment->mime, + 'size' => (int) $attachment->size, + 'links' => [ [ 'rel' => 'self', 'uri' => '/attachment/'.$attachment->id, diff --git a/app/Transformers/AvailableBudgetTransformer.php b/app/Transformers/AvailableBudgetTransformer.php index 100138acee..dacc389d07 100644 --- a/app/Transformers/AvailableBudgetTransformer.php +++ b/app/Transformers/AvailableBudgetTransformer.php @@ -36,11 +36,11 @@ use FireflyIII\Support\Facades\Amount; */ class AvailableBudgetTransformer extends AbstractTransformer { + private readonly bool $convertToNative; + private readonly TransactionCurrency $default; private readonly NoBudgetRepositoryInterface $noBudgetRepository; private readonly OperationsRepositoryInterface $opsRepository; private readonly BudgetRepositoryInterface $repository; - private readonly TransactionCurrency $default; - private readonly bool $convertToNative; /** * CurrencyTransformer constructor. diff --git a/app/Transformers/BillTransformer.php b/app/Transformers/BillTransformer.php index e793bc015e..ec152568f8 100644 --- a/app/Transformers/BillTransformer.php +++ b/app/Transformers/BillTransformer.php @@ -41,9 +41,9 @@ use Illuminate\Support\Collection; class BillTransformer extends AbstractTransformer { private readonly BillDateCalculator $calculator; - private readonly BillRepositoryInterface $repository; - private readonly TransactionCurrency $default; private readonly bool $convertToNative; + private readonly TransactionCurrency $default; + private readonly BillRepositoryInterface $repository; /** * BillTransformer constructor. diff --git a/app/Transformers/BudgetLimitTransformer.php b/app/Transformers/BudgetLimitTransformer.php index 1a4eaa5d3f..a1990febec 100644 --- a/app/Transformers/BudgetLimitTransformer.php +++ b/app/Transformers/BudgetLimitTransformer.php @@ -41,9 +41,8 @@ class BudgetLimitTransformer extends AbstractTransformer = [ 'budget', ]; - - protected TransactionCurrency $default; protected bool $convertToNative; + protected TransactionCurrency $default; public function __construct() { diff --git a/app/Transformers/BudgetTransformer.php b/app/Transformers/BudgetTransformer.php index 9dac0351bf..12e5feac28 100644 --- a/app/Transformers/BudgetTransformer.php +++ b/app/Transformers/BudgetTransformer.php @@ -38,10 +38,10 @@ use Symfony\Component\HttpFoundation\ParameterBag; */ class BudgetTransformer extends AbstractTransformer { - private readonly OperationsRepositoryInterface $opsRepository; - private readonly BudgetRepositoryInterface $repository; private readonly bool $convertToNative; private readonly TransactionCurrency $default; + private readonly OperationsRepositoryInterface $opsRepository; + private readonly BudgetRepositoryInterface $repository; /** * BudgetTransformer constructor. diff --git a/app/Transformers/CategoryTransformer.php b/app/Transformers/CategoryTransformer.php index 90ee854fcb..a5516e03c3 100644 --- a/app/Transformers/CategoryTransformer.php +++ b/app/Transformers/CategoryTransformer.php @@ -36,10 +36,10 @@ use Illuminate\Support\Collection; */ class CategoryTransformer extends AbstractTransformer { + private readonly bool $convertToNative; + private readonly TransactionCurrency $default; private readonly OperationsRepositoryInterface $opsRepository; private readonly CategoryRepositoryInterface $repository; - private readonly TransactionCurrency $default; - private readonly bool $convertToNative; /** * CategoryTransformer constructor. diff --git a/app/Transformers/TransactionGroupTransformer.php b/app/Transformers/TransactionGroupTransformer.php index af5bf4815f..787c5e74a3 100644 --- a/app/Transformers/TransactionGroupTransformer.php +++ b/app/Transformers/TransactionGroupTransformer.php @@ -83,14 +83,14 @@ class TransactionGroupTransformer extends AbstractTransformer $first = new NullArrayObject(reset($group['transactions'])); return [ - 'id' => (int) $first['transaction_group_id'], - 'created_at' => $first['created_at']->toAtomString(), - 'updated_at' => $first['updated_at']->toAtomString(), - 'user' => (string) $data['user_id'], - 'user_group' => (string) $data['user_group_id'], - 'group_title' => $data['title'], - 'transactions' => $this->transformTransactions($data), - 'links' => [ + 'id' => (int) $first['transaction_group_id'], + 'created_at' => $first['created_at']->toAtomString(), + 'updated_at' => $first['updated_at']->toAtomString(), + 'user' => (string) $data['user_id'], + 'user_group' => (string) $data['user_group_id'], + 'group_title' => $data['title'], + 'transactions' => $this->transformTransactions($data), + 'links' => [ [ 'rel' => 'self', 'uri' => '/transactions/'.$first['transaction_group_id'], @@ -231,12 +231,6 @@ class TransactionGroupTransformer extends AbstractTransformer return null; } - private function getLocation(TransactionJournal $journal): ?Location - { - /** @var null|Location */ - return $journal->locations()->first(); - } - /** * @throws FireflyException */ @@ -526,4 +520,10 @@ class TransactionGroupTransformer extends AbstractTransformer return $array; } + + private function getLocation(TransactionJournal $journal): ?Location + { + /** @var null|Location */ + return $journal->locations()->first(); + } } diff --git a/app/Transformers/V2/AccountTransformer.php b/app/Transformers/V2/AccountTransformer.php index c416a83ecc..2f71df27d4 100644 --- a/app/Transformers/V2/AccountTransformer.php +++ b/app/Transformers/V2/AccountTransformer.php @@ -44,13 +44,13 @@ class AccountTransformer extends AbstractTransformer private array $accountMeta; private array $accountTypes; private array $balanceDifferences; + private array $balances; private array $convertedBalances; private array $currencies; private TransactionCurrency $default; private array $fullTypes; private array $lastActivity; private array $objectGroups; - private array $balances; /** * This method collects meta-data for one or all accounts in the transformer's collection. diff --git a/app/Transformers/V2/BillTransformer.php b/app/Transformers/V2/BillTransformer.php index 34d9afe5e1..d5209d2243 100644 --- a/app/Transformers/V2/BillTransformer.php +++ b/app/Transformers/V2/BillTransformer.php @@ -143,9 +143,9 @@ class BillTransformer extends AbstractTransformer app('log')->debug(sprintf('Foreign currency is #%d', $transaction['foreign_currency_id'])); $foreignCurrencyId = (int) $transaction['foreign_currency_id']; $currencies[$foreignCurrencyId] ??= TransactionCurrency::find($foreignCurrencyId); - $foreignCurrencyCode = $currencies[$foreignCurrencyId]->code; // @phpstan-ignore property.notFound - $foreignCurrencyName = $currencies[$foreignCurrencyId]->name; // @phpstan-ignore property.notFound - $foreignCurrencySymbol = $currencies[$foreignCurrencyId]->symbol; // @phpstan-ignore property.notFound + $foreignCurrencyCode = $currencies[$foreignCurrencyId]->code; // @phpstan-ignore property.notFound + $foreignCurrencyName = $currencies[$foreignCurrencyId]->name; // @phpstan-ignore property.notFound + $foreignCurrencySymbol = $currencies[$foreignCurrencyId]->symbol; // @phpstan-ignore property.notFound $foreignCurrencyDp = $currencies[$foreignCurrencyId]->decimal_places; // @phpstan-ignore property.notFound } diff --git a/app/Transformers/V2/TransactionGroupTransformer.php b/app/Transformers/V2/TransactionGroupTransformer.php index 1a30321466..e23bd8506b 100644 --- a/app/Transformers/V2/TransactionGroupTransformer.php +++ b/app/Transformers/V2/TransactionGroupTransformer.php @@ -53,14 +53,14 @@ class TransactionGroupTransformer extends AbstractTransformer private array $currencies = []; private TransactionCurrency $default; // collection of all currencies for this transformer. private array $journals = []; - private array $objects = []; + private array $meta = []; // private array $currencies = []; // private array $transactionTypes = []; - private array $meta = []; - private array $notes = []; + private array $notes = []; + private array $objects = []; // private array $locations = []; - private array $tags = []; + private array $tags = []; // private array $amounts = []; // private array $foreignAmounts = []; // private array $journalCurrencies = []; diff --git a/app/Validation/AccountValidator.php b/app/Validation/AccountValidator.php index 128f851a05..456d0df50b 100644 --- a/app/Validation/AccountValidator.php +++ b/app/Validation/AccountValidator.php @@ -49,14 +49,14 @@ class AccountValidator use TransferValidation; use WithdrawalValidation; - public bool $createMode; - public string $destError; - public ?Account $destination; - public ?Account $source; - public string $sourceError; - private AccountRepositoryInterface $accountRepository; - private array $combinations; - private string $transactionType; + public bool $createMode; + public string $destError; + public ?Account $destination; + public ?Account $source; + public string $sourceError; + private AccountRepositoryInterface $accountRepository; + private array $combinations; + private string $transactionType; /** * AccountValidator constructor. diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index 6f9ae81c09..8b06e056c3 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -104,7 +104,7 @@ class FireflyValidator extends Validator /** * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ - public function validateBic(mixed $attribute, mixed $value): bool + public function validateBic(mixed $attribute, mixed $value): bool { if (!is_string($value) || strlen($value) < 8) { return false; @@ -857,8 +857,7 @@ class FireflyValidator extends Validator ->where('trigger', $trigger) ->where('response', $response) ->where('delivery', $delivery) - ->where('url', $url)->count() - ; + ->where('url', $url)->count(); } return false;